diff --git a/tests/application/discovery/__init__.py b/tests/application/discovery/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/application/discovery/sample.py b/tests/application/discovery/sample.py new file mode 100644 index 00000000..947a9b0f --- /dev/null +++ b/tests/application/discovery/sample.py @@ -0,0 +1,5 @@ +from .sample_helpers import SampleHelper + + +class Sample: + helper = SampleHelper diff --git a/tests/application/discovery/sample_helpers.py b/tests/application/discovery/sample_helpers.py new file mode 100644 index 00000000..da45ac6c --- /dev/null +++ b/tests/application/discovery/sample_helpers.py @@ -0,0 +1,2 @@ +class SampleHelper: + pass diff --git a/tests/application/test_module_discovery.py b/tests/application/test_module_discovery.py index dfb48e0d..611accbf 100644 --- a/tests/application/test_module_discovery.py +++ b/tests/application/test_module_discovery.py @@ -30,3 +30,21 @@ def test_all_subclasses_with_exception(reference_type): reference_type.side_effect = TypeError exception_subclasses = list(get_all_subclasses(Exception)) assert not exception_subclasses + + +def test_get_all_classes_of_module(): + from tests.application.discovery.sample import Sample + + found_classes = list(get_all_classes('tests.application.discovery.sample')) + + # SampleHelper is imported into the module but defined in a module whose name only starts the same + assert found_classes == [('Sample', Sample)] + + +def test_get_all_classes_of_package(): + from tests.application.discovery.sample import Sample + from tests.application.discovery.sample_helpers import SampleHelper + + found_classes = list(get_all_classes('tests.application.discovery')) + + assert sorted(found_classes) == [('Sample', Sample), ('SampleHelper', SampleHelper)] diff --git a/tests/web/test_autodiscovery.py b/tests/web/test_autodiscovery.py index 6ba0147f..43395824 100644 --- a/tests/web/test_autodiscovery.py +++ b/tests/web/test_autodiscovery.py @@ -7,3 +7,11 @@ def test_find_package_routes(): # Assert assert [route.url_path for route in routes] == ['api_1/', 'api_2/', 'api_2/', 'api_3/'] + + +def test_find_module_routes(): + # Act + routes = winter.web.find_package_routes('tests.api.package.api_2') + + # Assert + assert [route.url_path for route in routes] == ['api_2/', 'api_2/'] diff --git a/winter/application/module_discovery.py b/winter/application/module_discovery.py index b139d708..8530d294 100644 --- a/winter/application/module_discovery.py +++ b/winter/application/module_discovery.py @@ -9,13 +9,14 @@ def get_all_classes(package: str) -> Generator[Tuple[str, Type], None, None]: + """Classes defined in the modules of a package, or in a single module.""" classes_set = set() for module in import_recursively(package): try: for class_name, class_ in inspect.getmembers(module, inspect.isclass): if class_ in classes_set: continue - if not class_.__module__.startswith(package): + if class_.__module__ != package and not class_.__module__.startswith(package + '.'): continue yield class_name, class_ classes_set.add(class_) @@ -41,6 +42,10 @@ def get_all_subclasses(supertype: Type) -> Generator[Tuple[str, Type], None, Non def import_recursively(package: str) -> Generator[ModuleType, None, None]: + """Imports and yields the modules of a package and its subpackages, or a single module itself.""" package = importlib.import_module(package) + if not hasattr(package, '__path__'): + yield package + return for loader, name, is_pkg in pkgutil.walk_packages(package.__path__, package.__name__ + '.'): yield importlib.import_module(name) diff --git a/winter/web/autodiscovery.py b/winter/web/autodiscovery.py index 3c7e7cb2..70ff29ce 100644 --- a/winter/web/autodiscovery.py +++ b/winter/web/autodiscovery.py @@ -8,6 +8,7 @@ def find_package_routes(package_name: str) -> List[Route]: + """Routes of the API classes in a package and its subpackages, or in a single module.""" import_recursively(package_name) routes = []