diff --git a/skillbridge/__init__.py b/skillbridge/__init__.py index 23eb854..5e95a92 100644 --- a/skillbridge/__init__.py +++ b/skillbridge/__init__.py @@ -69,6 +69,8 @@ def generate_static_completion() -> None: verbose=True, quiet=False, export_less=False, + inspect=False, + include_docstrings=False, ) generate_stubs(o) diff --git a/skillbridge/client/objects.py b/skillbridge/client/objects.py index ed49999..5972ec8 100644 --- a/skillbridge/client/objects.py +++ b/skillbridge/client/objects.py @@ -38,7 +38,37 @@ def is_jupyter_magic(attribute: str) -> bool: return attribute in ignore -class RemoteObject(RemoteVariable): +class WithAttributeAccess(RemoteVariable): + def __getattr__(self, key: str) -> Any: + if is_jupyter_magic(key): + raise AttributeError(key) + + result = self._send(self._translator.encode_getattr(self._variable, key)) + return self._translator.decode(result) + + def __setattr__(self, key: str, value: Any) -> None: + if key in remote_variable_attributes: + return super().__setattr__(key, value) + + result = self._send(self._translator.encode_setattr(self._variable, key, value)) + self._translator.decode(result) + return None + + def __dir__(self) -> Iterable[str]: + if self._is_open_file(): + return super().__dir__() + + response = self._send(self._translator.encode_dir(self._variable)) + return self._translator.decode_dir(response) + + def _send(self, command: SkillCode) -> Any: + return self._channel.send(command).strip() + + def _is_open_file(self) -> bool: + return False + + +class RemoteObject(WithAttributeAccess, RemoteVariable): @property def skill_id(self) -> int: address = self._variable[5:].rsplit('_', maxsplit=1)[1] @@ -71,9 +101,6 @@ def skill_type(self) -> str | None: return typ.name[2:-4] return cast(str, typ) - def _send(self, command: SkillCode) -> Any: - return self._channel.send(command).strip() - def __str__(self) -> str: typ = self.skill_type or self.skill_parent_type if typ == 'open_file': @@ -85,32 +112,10 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"" - def __dir__(self) -> Iterable[str]: - if self._is_open_file(): - return super().__dir__() - - response = self._send(self._translator.encode_dir(self._variable)) - return self._translator.decode_dir(response) - - def __getattr__(self, key: str) -> Any: - if is_jupyter_magic(key): - raise AttributeError(key) - - result = self._send(self._translator.encode_getattr(self._variable, key)) - return self._translator.decode(result) - def __getitem__(self, item: str) -> Any: result = self._send(self._translator.encode_getattr(self._variable, item, lambda x: x)) return self._translator.decode(result) - def __setattr__(self, key: str, value: Any) -> None: - if key in remote_variable_attributes: - return super().__setattr__(key, value) - - result = self._send(self._translator.encode_setattr(self._variable, key, value)) - self._translator.decode(result) - return None - def __setitem__(self, key: str, value: Any) -> None: result = self._send( self._translator.encode_setattr(self._variable, key, value, lambda x: x), @@ -246,7 +251,7 @@ def __iter__(self) -> Iterator[Skill]: return iter(self._translator.decode(result) or ()) # type: ignore[arg-type] -class RemoteVector(RemoteCollection): +class RemoteVector(RemoteCollection, WithAttributeAccess): def __getitem__(self, item: Skill) -> Skill: try: return super().__getitem__(item) diff --git a/tests/test_integration.py b/tests/test_integration.py index adc6dab..d28992f 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -17,7 +17,6 @@ def ws() -> Workspace: except (Exception, ValueError, AssertionError): warn("Skipping integration tests, because Workspace could not connect", UserWarning) skip() - raise # this line is unreachable but mypy and pycharm don't know that return workspace @@ -235,3 +234,13 @@ def test_run_script_blocks_when_requested(ws: Workspace) -> None: assert ws['pyRunScript'](str(here / 'script.py'), args=(variable, '42', '0.25'), block=True) assert ws['plus'](Var(variable), 1) == 43 + + +def test_form_vectors_have_dir(ws: Workspace) -> None: + form = ws.hi.get_current_form() + assert 'button_layout' in dir(form) + + +def test_form_vectors_have_getattr(ws: Workspace) -> None: + form = ws.hi.get_current_form() + assert isinstance(form.button_layout, list)