diff --git a/poetry.lock b/poetry.lock index 087a988..3fe86db 100644 --- a/poetry.lock +++ b/poetry.lock @@ -559,31 +559,48 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "lazy-object-proxy" -version = "1.8.0" +version = "1.9.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"}, - {file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"}, - {file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"}, - {file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"}, + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, ] [[package]] @@ -1204,6 +1221,17 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "stringcase" +version = "1.2.0" +description = "String case converter." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"}, +] + [[package]] name = "termcolor" version = "2.2.0" @@ -1727,4 +1755,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.7.2, <4.0" -content-hash = "7bc3a728e6f2215e451f522d50ed16fd547f32b0bafce7176b5f18c28b499f4e" +content-hash = "08a9cab6af36721f5cf65834281b641f93e60978fc54340f222c833a88aa5bb3" diff --git a/pyproject.toml b/pyproject.toml index bfe25a1..47cc278 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ appdirs = "^1.4.4" gql = {extras = ["requests", "websockets"], version = "^3.3.0"} ujson = "^5.3.0" Deprecated = "^1.2.13" +stringcase = "^1.2.0" [tool.poetry.group.dev.dependencies] black = "^22.8.0" diff --git a/src/specklepy/objects/base.py b/src/specklepy/objects/base.py index 1115068..a56bc84 100644 --- a/src/specklepy/objects/base.py +++ b/src/specklepy/objects/base.py @@ -15,6 +15,8 @@ from typing import ( ) from warnings import warn +from stringcase import pascalcase + from specklepy.logging.exceptions import SpeckleException from specklepy.objects.units import Units, get_units_from_string from specklepy.transports.memory import MemoryTransport @@ -92,6 +94,7 @@ class _RegisteringBase: speckle_type: ClassVar[str] _speckle_type_override: ClassVar[Optional[str]] = None + _speckle_namespace: ClassVar[Optional[str]] = None _type_registry: ClassVar[Dict[str, Type["Base"]]] = {} _attr_types: ClassVar[Dict[str, Type]] = {} # dict of chunkable props and their max chunk size @@ -103,7 +106,11 @@ class _RegisteringBase: @classmethod def get_registered_type(cls, speckle_type: str) -> Optional[Type["Base"]]: """Get the registered type from the protected mapping via the `speckle_type`""" - return cls._type_registry.get(speckle_type, None) + for full_name in reversed(speckle_type.split(":")): + maybe_type = cls._type_registry.get(full_name, None) + if maybe_type: + return maybe_type + return None @classmethod def _determine_speckle_type(cls) -> str: @@ -122,12 +129,29 @@ class _RegisteringBase: return base_name bases = [ - b._speckle_type_override if b._speckle_type_override else b.__name__ + b._full_name() for b in reversed(cls.mro()) if issubclass(b, Base) and b.__name__ != base_name ] return ":".join(bases) + @classmethod + def _full_name(cls) -> str: + base_name = "Base" + if cls.__name__ == base_name: + return base_name + + if cls._speckle_type_override: + return cls._speckle_type_override + + # convert the module names to PascalCase to match c# namespace naming convention + # also drop specklepy from the beginning + namespace = ".".join( + pascalcase(m) + for m in filter(lambda name: name != "specklepy", cls.__module__.split(".")) + ) + return f"{namespace}.{cls.__name__}" + def __init_subclass__( cls, speckle_type: Optional[str] = None, @@ -145,13 +169,13 @@ class _RegisteringBase: """ cls._speckle_type_override = speckle_type cls.speckle_type = cls._determine_speckle_type() - if cls.speckle_type in cls._type_registry: + if cls._full_name() in cls._type_registry: raise ValueError( f"The speckle_type: {speckle_type} is already registered for type: " - f"{cls._type_registry[cls.speckle_type].__name__}. " + f"{cls._type_registry[cls._full_name()].__name__}. " "Please choose a different type name." ) - cls._type_registry[cls.speckle_type] = cls # type: ignore + cls._type_registry[cls._full_name()] = cls # type: ignore try: cls._attr_types = get_type_hints(cls) except Exception: diff --git a/src/specklepy/objects/structural/__init__.py b/src/specklepy/objects/structural/__init__.py index c7bdae9..9690754 100644 --- a/src/specklepy/objects/structural/__init__.py +++ b/src/specklepy/objects/structural/__init__.py @@ -34,7 +34,7 @@ from specklepy.objects.structural.loading import ( LoadNode, LoadType, ) -from specklepy.objects.structural.material import ( +from specklepy.objects.structural.materials import ( Concrete, MaterialType, Steel, diff --git a/src/specklepy/objects/structural/material.py b/src/specklepy/objects/structural/materials.py similarity index 94% rename from src/specklepy/objects/structural/material.py rename to src/specklepy/objects/structural/materials.py index a18e8d1..ab0abcd 100644 --- a/src/specklepy/objects/structural/material.py +++ b/src/specklepy/objects/structural/materials.py @@ -40,7 +40,7 @@ class StructuralMaterial( materialSafetyFactor: float = 0.0 -class Concrete(StructuralMaterial, speckle_type=STRUCTURAL_MATERIALS + ".Concrete"): +class Concrete(StructuralMaterial): compressiveStrength: float = 0.0 tensileStrength: float = 0.0 flexuralStrength: float = 0.0 diff --git a/src/specklepy/objects/structural/properties.py b/src/specklepy/objects/structural/properties.py index 2d10780..2402163 100644 --- a/src/specklepy/objects/structural/properties.py +++ b/src/specklepy/objects/structural/properties.py @@ -3,7 +3,7 @@ from typing import Optional from specklepy.objects.base import Base from specklepy.objects.structural.axis import Axis -from specklepy.objects.structural.material import StructuralMaterial +from specklepy.objects.structural.materials import StructuralMaterial STRUCTURAL_PROPERTY = "Objectives.Structural.Properties" diff --git a/tests/unit/test_registering_base.py b/tests/unit/test_registering_base.py index b3f3055..d914a7e 100644 --- a/tests/unit/test_registering_base.py +++ b/tests/unit/test_registering_base.py @@ -22,9 +22,9 @@ class Baz(Bar): "klass, speckle_type", [ (Base, "Base"), - (Foo, "Foo"), - (Bar, "Foo:Custom.Bar"), - (Baz, "Foo:Custom.Bar:Baz"), + (Foo, "Tests.TestRegisteringBase.Foo"), + (Bar, "Tests.TestRegisteringBase.Foo:Custom.Bar"), + (Baz, "Tests.TestRegisteringBase.Foo:Custom.Bar:Tests.TestRegisteringBase.Baz"), ( Concrete, "Objects.Structural.Materials.StructuralMaterial:Objects.Structural.Materials.Concrete", @@ -33,3 +33,15 @@ class Baz(Bar): ) def test_determine_speckle_type(klass: Type[Base], speckle_type: str): assert klass._determine_speckle_type() == speckle_type + + +@pytest.mark.parametrize( + "klass, fully_qualified_name", + [ + (Base, "Base"), + (Foo, "Tests.TestRegisteringBase.Foo"), + (Concrete, "Objects.Structural.Materials.Concrete"), + ], +) +def test_full_name(klass: Type[Base], fully_qualified_name: str): + assert klass._full_name() == fully_qualified_name diff --git a/tests/unit/test_structural.py b/tests/unit/test_structural.py index 95ea1b0..84de472 100644 --- a/tests/unit/test_structural.py +++ b/tests/unit/test_structural.py @@ -11,7 +11,7 @@ from specklepy.objects.structural.geometry import ( Restraint, ) from specklepy.objects.structural.loading import LoadGravity -from specklepy.objects.structural.material import StructuralMaterial +from specklepy.objects.structural.materials import StructuralMaterial from specklepy.objects.structural.properties import ( MemberType, Property1D, diff --git a/tests/unit/test_traverse_value.py b/tests/unit/test_traverse_value.py index 74f49b5..99e8f88 100644 --- a/tests/unit/test_traverse_value.py +++ b/tests/unit/test_traverse_value.py @@ -16,7 +16,7 @@ def test_traverse_value(): object_id, object_dict = serializer.traverse_base(base) assert object_dict == { "id": object_id, - "speckle_type": "FakeBase", + "speckle_type": "Tests.TestTraverseValue.FakeBase", "applicationId": None, "foo": [None], "units": None,