| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from copy import deepcopy |
| |
| |
| def deepcopy_with_locators(value): |
| """ |
| Like :func:`deepcopy`, but also copies over locators. |
| """ |
| |
| res = deepcopy(value) |
| copy_locators(res, value) |
| return res |
| |
| |
| def copy_locators(target, source): |
| """ |
| Copies over ``_locator`` for all elements, recursively. |
| |
| Assumes that target and source have exactly the same list/dict structure. |
| """ |
| |
| locator = getattr(source, '_locator', None) |
| if locator is not None: |
| try: |
| setattr(target, '_locator', locator) |
| except AttributeError: |
| pass |
| |
| if isinstance(target, list) and isinstance(source, list): |
| for i, _ in enumerate(target): |
| copy_locators(target[i], source[i]) |
| elif isinstance(target, dict) and isinstance(source, dict): |
| for k, v in target.items(): |
| copy_locators(v, source[k]) |