diff --git a/bindings/pydrake/__init__.py b/bindings/pydrake/__init__.py index 9f11515be83f..beeadbfa6f12 100644 --- a/bindings/pydrake/__init__.py +++ b/bindings/pydrake/__init__.py @@ -53,6 +53,9 @@ def getDrakePath(): def _execute_extra_python_code(m): # See `ExecuteExtraPythonCode` in `pydrake_pybind.h` for usage details and # rationale. + if m.__name__ not in sys.modules: + # N.B. This is necessary for C++ extensions in Python 3. + sys.modules[m.__name__] = m module_path = m.__name__.split(".") if len(module_path) == 1: raise RuntimeError(( diff --git a/bindings/pydrake/common/cpp_template.py b/bindings/pydrake/common/cpp_template.py index dda904efc18e..00e8cf3e700d 100644 --- a/bindings/pydrake/common/cpp_template.py +++ b/bindings/pydrake/common/cpp_template.py @@ -278,7 +278,7 @@ def _on_add(self, param, instantiation): return instantiation @classmethod - def define(cls, name, param_list, *args, **kwargs): + def define(cls, name, param_list, *args, scope=None, **kwargs): """Provides a decorator for functions that defines a template using `name`. The template instantiations are added using `add_instantiations`, where the instantiation function is the decorated @@ -308,7 +308,9 @@ def __init__(self): self.T = T return Impl """ - template = cls(name, *args, **kwargs) + if scope is None: + scope = _get_module_from_stack() + template = cls(name, *args, scope=scope, **kwargs) def decorator(instantiation_func): template.add_instantiations(instantiation_func, param_list) diff --git a/bindings/pydrake/common/test/cpp_template_test.py b/bindings/pydrake/common/test/cpp_template_test.py index 2e42f5c76e4b..c2e41ef12f1b 100644 --- a/bindings/pydrake/common/test/cpp_template_test.py +++ b/bindings/pydrake/common/test/cpp_template_test.py @@ -152,6 +152,8 @@ def __mangled_method(self): return Impl + self.assertEqual( + str(MyTemplate), f"") self.assertIsInstance(MyTemplate, m.TemplateClass) MyDefault = MyTemplate[None] MyInt = MyTemplate[int] diff --git a/bindings/pydrake/systems/scalar_conversion.py b/bindings/pydrake/systems/scalar_conversion.py index e422f48abbbc..bb620c0a8e93 100644 --- a/bindings/pydrake/systems/scalar_conversion.py +++ b/bindings/pydrake/systems/scalar_conversion.py @@ -110,7 +110,8 @@ def __init__(self, name, T_list=None, T_pairs=None, scope=None): self._converter = self._make_converter() @classmethod - def define(cls, name, T_list=None, T_pairs=None, *args, **kwargs): + def define( + cls, name, T_list=None, T_pairs=None, *args, scope=None, **kwargs): """Provides a decorator which can be used define a scalar-type convertible System as a template. @@ -126,7 +127,9 @@ class which will be the instantiation for type ``T`` of the given args, kwargs: These are passed to the constructor of ``TemplateSystem``. """ - template = cls(name, T_list, T_pairs, *args, **kwargs) + if scope is None: + scope = _get_module_from_stack() + template = cls(name, T_list, T_pairs, *args, scope=scope, **kwargs) param_list = [(T,) for T in template._T_list] def decorator(instantiation_func): diff --git a/bindings/pydrake/systems/test/scalar_conversion_test.py b/bindings/pydrake/systems/test/scalar_conversion_test.py index 77db487c5ef0..bddc30afb0f0 100644 --- a/bindings/pydrake/systems/test/scalar_conversion_test.py +++ b/bindings/pydrake/systems/test/scalar_conversion_test.py @@ -58,6 +58,8 @@ def test_example_system(self): """Tests the Example_ system.""" # Test template. self.assertIsInstance(Example_, TemplateClass) + self.assertEqual( + str(Example_), f"") self.assertIs(Example_[float], Example) # Test parameters. @@ -68,7 +70,7 @@ def test_example_system(self): system_T = Example_[T](0) self.assertEqual( system_T.GetSystemType(), - f"pydrake.systems.scalar_conversion.Example_[{T.__name__}]") + f"{__name__}.Example_[{T.__name__}]") # Test private properties (do NOT use these in your code!). self.assertTupleEqual(