| 1025 | return cloned_traverse(obj, opts, visitors) |
| 1026 | |
| 1027 | def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible: |
| 1028 | if elem in stop_on: |
| 1029 | return elem |
| 1030 | else: |
| 1031 | if id(elem) not in cloned: |
| 1032 | if "replace" in kw: |
| 1033 | newelem = cast( |
| 1034 | Optional[ExternallyTraversible], kw["replace"](elem) |
| 1035 | ) |
| 1036 | if newelem is not None: |
| 1037 | cloned[id(elem)] = newelem |
| 1038 | return newelem |
| 1039 | |
| 1040 | # the _clone method for immutable normally returns "self". |
| 1041 | # however, the method is still allowed to return a |
| 1042 | # different object altogether; ColumnClause._clone() will |
| 1043 | # based on options clone the subquery to which it is associated |
| 1044 | # and return the new corresponding column. |
| 1045 | cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw) |
| 1046 | newelem._copy_internals(clone=clone, **kw) |
| 1047 | |
| 1048 | # however, visit methods which are tasked with in-place |
| 1049 | # mutation of the object should not get access to the immutable |
| 1050 | # object. |
| 1051 | if not elem._is_immutable: |
| 1052 | meth = visitors.get(newelem.__visit_name__, None) |
| 1053 | if meth: |
| 1054 | meth(newelem) |
| 1055 | return cloned[id(elem)] |
| 1056 | |
| 1057 | if obj is not None: |
| 1058 | obj = clone( |