Skip to content

Commit

Permalink
Fix ButlerURI inheritance
Browse files Browse the repository at this point in the history
This change inserts ButlerURI into the ResourcePath class
inheritance by repointing the base class.
  • Loading branch information
timj committed Jan 10, 2022
1 parent 3f8f608 commit 17829e4
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/mypy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
run: pip install "mypy==0.910" pydantic

- name: Install third party stubs
run: pip install types-requests types-PyYAML types-click types-pkg_resources
run: pip install types-requests types-PyYAML types-click types-pkg_resources types-Deprecated

- name: Install lsst.utils
run: |
Expand Down
44 changes: 42 additions & 2 deletions python/lsst/daf/butler/core/_butlerUri.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
# Temporarily alias ResourcePath as ButlerURI.
from typing import Any
from typing import Any, Type

from deprecated.sphinx import deprecated
from lsst.resources import ResourcePath, ResourcePathExpression
from lsst.resources.file import FileResourcePath


def _add_base(cls: Type) -> None:
"""Update the class to use ButlerURI as the base.
Notes
-----
Care must be taken with SchemelessResourcePath since that class inherits
from FileResourcePath.
"""
if cls.__bases__ == (ResourcePath,):
cls.__bases__ = (ButlerURI,)
elif cls.__bases__ == (FileResourcePath,) and FileResourcePath.__bases__ == (ResourcePath,):
FileResourcePath.__bases__ = (ButlerURI,)


def _reset_base(cls: Type) -> None:
"""Reset the base class to be ResourcePath."""
if cls.__bases__ == (ButlerURI,):
cls.__bases__ = (ResourcePath,)
elif issubclass(cls, FileResourcePath) and FileResourcePath.__bases__ == (ButlerURI,):
FileResourcePath.__bases__ = (ResourcePath,)


@deprecated(
Expand All @@ -17,4 +40,21 @@ class ButlerURI(ResourcePath):
"""

def __new__(cls, uri: ResourcePathExpression, **kwargs: Any) -> ResourcePath: # type: ignore
return ResourcePath(uri, **kwargs)
if cls is not ButlerURI:
# This is a subclass trying to create an updated version of
# itself without wanting to change the class. The ButlerURI
# __new__ forces ResourcePath() to be used every time and so
# the knowledge of the subclass is lost. This can result in a
# Schemeless becoming a File URI. The simplest approach is to
# remove ButlerURI from the class hierarchy and add it back again.
# This seems inefficient but without it the subclass will
# always try to call ButlerURI.__new__.
_reset_base(cls)
new = cls(uri, **kwargs)
_add_base(cls)
return new

new = ResourcePath(uri, **kwargs)
_add_base(type(new))

return new
1 change: 1 addition & 0 deletions tests/test_uri.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def tearDown(self):
def testFile(self):
file = os.path.join(self.tmpdir, "test.txt")
uri = ButlerURI(file)
self.assertIsInstance(uri, ButlerURI)
self.assertFalse(uri.exists(), f"{uri} should not exist")
self.assertEqual(uri.ospath, file)

Expand Down

0 comments on commit 17829e4

Please sign in to comment.