Skip to content

Commit 9a9c3a2

Browse files
committed
fix for constructingn D3PluginClient
1 parent 720a7bb commit 9a9c3a2

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

src/designer_plugin/d3sdk/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ def __call__(cls, *args, **kwargs): # type: ignore
302302
Returns:
303303
An instance of the plugin class with instance_code attribute set
304304
"""
305+
# Base class (and any non-instrumented subclasses) don't carry
306+
# remote-instantiation metadata; fall back to normal construction.
307+
if not hasattr(cls, "filtered_init_args"):
308+
return super().__call__(*args, **kwargs)
309+
305310
# Build argument string for remote instantiation, respecting defaults.
306311
param_names: list[str] = cls.filtered_init_args
307312
arg_strings: list[str] = []

tests/test_client.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,89 @@ def test_method(self) -> str:
895895
assert '99' in plugin3.instance_code
896896

897897

898+
class TestMetaclassCallGuard:
899+
"""Test suite for metaclass __call__ guard for base/non-instrumented classes.
900+
901+
This addresses the issue where D3PluginClientMeta.__new__ skips instrumentation
902+
when name == "D3PluginClient", so the base class never gets filtered_init_args/
903+
instance_code. However, __call__ unconditionally reads cls.filtered_init_args,
904+
causing AttributeError when instantiating D3PluginClient() directly or any
905+
non-instrumented class.
906+
"""
907+
908+
def test_base_class_instantiation_without_args(self):
909+
"""Test that base D3PluginClient can be instantiated without arguments.
910+
911+
This would raise AttributeError without the guard in __call__.
912+
"""
913+
# This should not raise AttributeError
914+
plugin = D3PluginClient()
915+
916+
# Verify the instance is created properly
917+
assert isinstance(plugin, D3PluginClient)
918+
assert hasattr(plugin, '_hostname')
919+
assert hasattr(plugin, '_port')
920+
assert hasattr(plugin, '_override_module_name')
921+
922+
def test_base_class_does_not_have_instrumentation_attributes(self):
923+
"""Test that base D3PluginClient class lacks instrumentation attributes."""
924+
# The base class should not have these attributes
925+
assert not hasattr(D3PluginClient, 'filtered_init_args')
926+
assert not hasattr(D3PluginClient, 'source_code')
927+
assert not hasattr(D3PluginClient, 'source_code_py27')
928+
929+
def test_instrumented_subclass_has_attributes(self):
930+
"""Test that instrumented subclasses do have instrumentation attributes."""
931+
class InstrumentedPlugin(D3PluginClient):
932+
def __init__(self, a: int):
933+
super().__init__()
934+
self.a = a
935+
936+
def test_method(self) -> int:
937+
return self.a
938+
939+
# Instrumented subclasses should have these attributes
940+
assert hasattr(InstrumentedPlugin, 'filtered_init_args')
941+
assert hasattr(InstrumentedPlugin, 'source_code')
942+
assert hasattr(InstrumentedPlugin, 'source_code_py27')
943+
944+
def test_instrumented_subclass_instantiation_works(self):
945+
"""Test that instrumented subclasses can still be instantiated normally."""
946+
class InstrumentedPlugin(D3PluginClient):
947+
def __init__(self, a: int, b: str = "default"):
948+
super().__init__()
949+
self.a = a
950+
self.b = b
951+
952+
def test_method(self) -> str:
953+
return f"{self.a}:{self.b}"
954+
955+
# This should work as before
956+
plugin = InstrumentedPlugin(42, "test")
957+
958+
assert isinstance(plugin, InstrumentedPlugin)
959+
assert plugin.a == 42
960+
assert plugin.b == "test"
961+
962+
# Should have instance_code generated
963+
assert hasattr(plugin, 'instance_code')
964+
assert 'InstrumentedPlugin' in plugin.instance_code
965+
assert '42' in plugin.instance_code
966+
967+
def test_base_class_instance_no_instance_code(self):
968+
"""Test that base D3PluginClient instance doesn't get instance_code.
969+
970+
Since the base class bypasses instrumentation, instances shouldn't
971+
have instance_code attribute (or it should be handled gracefully).
972+
"""
973+
plugin = D3PluginClient()
974+
975+
# Base class instances shouldn't have instance_code
976+
# (or if they do, it should be handled gracefully)
977+
# The important thing is no AttributeError during instantiation
978+
assert isinstance(plugin, D3PluginClient)
979+
980+
898981
class TestInitWrapperSuperCall:
899982
"""Test suite for automatic parent __init__ calling when user forgets super().__init__()."""
900983

0 commit comments

Comments
 (0)