diff --git a/dvc/path_info.py b/dvc/path_info.py index c905f47fe..c7d0051d8 100644 --- a/dvc/path_info.py +++ b/dvc/path_info.py @@ -184,6 +184,12 @@ class URLInfo(object): __truediv__ = __div__ def __getattr__(self, name): + # When deepcopy is called, it creates and object without __init__, + # self.parsed is not initialized and it causes infinite recursion. + # More on this special casing here: + # https://stackoverflow.com/a/47300262/298182 + if name.startswith("__"): + raise AttributeError(name) return getattr(self.parsed, name) @cached_property diff --git a/tests/unit/test_path_info.py b/tests/unit/test_path_info.py index 46bd032b9..9ec73389b 100644 --- a/tests/unit/test_path_info.py +++ b/tests/unit/test_path_info.py @@ -1,4 +1,5 @@ import pytest +import copy from dvc.path_info import URLInfo, CloudURLInfo @@ -34,3 +35,10 @@ def test_url_info_parents(cls): cls("ssh://user@test.com/test1"), cls("ssh://user@test.com/"), ] + + +@pytest.mark.parametrize("cls", [URLInfo, CloudURLInfo]) +def test_url_info_deepcopy(cls): + u1 = cls("ssh://user@test.com:/test1/test2/test3") + u2 = copy.deepcopy(u1) + assert u1 == u2