Skip to content

Commit 5e83e8a

Browse files
Replace ModuleDiscovery class with plain functions and reduce hardcode amount (#233)
1 parent f51f25f commit 5e83e8a

File tree

7 files changed

+76
-23
lines changed

7 files changed

+76
-23
lines changed

.github/workflows/testing.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Install and configure Poetry
1919
uses: snok/install-poetry@v1
2020
with:
21-
version: latest
21+
version: 1.1.10
2222
- name: Install dependencies
2323
run: poetry install
2424
- name: Test with pytest

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [11.0.0]
8+
### Added
9+
- get_all_subclasses function in module_discovery module
10+
### Changed
11+
- Refactor ModuleDiscovery to functions
12+
713
## [10.0.1]
814
- Register ThrottleException as a global to simplify projects without throttling
915
- Add Swagger UI which do not rely on drf_yasg and static file server: winter_openapi.get_swagger_ui_html

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "winter"
3-
version = "10.0.1"
3+
version = "11.0.0"
44
homepage = "https://github.com/WinterFramework/winter"
55
description = "Web Framework inspired by Spring Framework"
66
authors = ["Alexander Egorov <[email protected]>"]

tests/core/test_module_discovery.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import mock
2+
3+
from winter.core import WinterApplication
4+
from winter.core.module_discovery import get_all_subclasses
5+
from winter.core.module_discovery import get_all_classes
6+
7+
8+
def test_module_import():
9+
found_classes = get_all_classes()
10+
# Verifying that some class (it can be any class) could be found
11+
assert ('WinterApplication', WinterApplication) in found_classes
12+
13+
14+
@mock.patch('inspect.getmembers')
15+
def test_module_import_with_exception(getmembers):
16+
# Verifying that import error during search of class will not stop the search
17+
getmembers.side_effect = ImportError
18+
found_classes = get_all_classes()
19+
# It can't find any class, beacuse we have ImportError on any class
20+
assert not found_classes
21+
22+
23+
def test_all_subclasses():
24+
exception_subclasses = get_all_subclasses(Exception)
25+
assert ('OverflowError', OverflowError) in exception_subclasses
26+
27+
28+
@mock.patch('_weakref.ReferenceType')
29+
def test_all_subclasses_with_exception(reference_type):
30+
reference_type.side_effect = TypeError
31+
exception_subclasses = get_all_subclasses(Exception)
32+
assert not exception_subclasses

winter/core/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@
1111
from .component_method_argument import ComponentMethodArgument
1212
from .injection import get_injector
1313
from .injection import set_injector
14-
from .module_discovery import ModuleDiscovery
1514
from .utils import cached_property

winter/core/module_discovery.py

+32-16
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,43 @@
77
from typing import Type
88

99

10-
class ModuleDiscovery:
11-
def get_all_classes(self, package: str = None) -> List[Tuple[str, Type]]:
12-
classes = []
13-
classes_set = set()
14-
for module_name, module in dict(sys.modules).items():
15-
if module_name.endswith('six.moves'): # pragma: no cover
16-
continue
10+
def get_all_classes(package: str = None) -> List[Tuple[str, Type]]:
11+
classes = []
12+
classes_set = set()
13+
for module_name, module in dict(sys.modules).items():
14+
try:
1715
for class_name, class_ in inspect.getmembers(module, inspect.isclass):
1816
if class_ in classes_set:
1917
continue
2018
if package and not (isinstance(class_.__module__, str) and class_.__module__.startswith(package)):
2119
continue
2220
classes.append((class_name, class_))
2321
classes_set.add(class_)
24-
return classes
22+
except ImportError:
23+
pass
24+
return classes
2525

26-
def import_recursively(self, package_name: str):
27-
package = importlib.import_module(package_name)
28-
for module in pkgutil.iter_modules(package.__path__):
29-
child_name = package_name + '.' + module.name
30-
if module.ispkg:
31-
self.import_recursively(child_name)
32-
else:
33-
importlib.import_module(child_name)
26+
27+
def get_all_subclasses(supertype: Type) -> List[Tuple[str, Type]]:
28+
classes = []
29+
for class_name, class_ in get_all_classes():
30+
try:
31+
# Workaround to not fail in the issubclass check
32+
from _weakref import ReferenceType
33+
34+
ReferenceType(class_)
35+
except TypeError:
36+
continue
37+
if class_ is not supertype and issubclass(class_, supertype):
38+
classes.append((class_name, class_))
39+
return classes
40+
41+
42+
def import_recursively(package_name: str):
43+
package = importlib.import_module(package_name)
44+
for module in pkgutil.iter_modules(package.__path__):
45+
child_name = package_name + '.' + module.name
46+
if module.ispkg:
47+
import_recursively(child_name)
48+
else:
49+
importlib.import_module(child_name)

winter_django/autodiscovery.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import winter_django
2-
3-
from winter.core import ModuleDiscovery
2+
from winter.core.module_discovery import import_recursively
3+
from winter.core.module_discovery import get_all_classes
44

55

66
def create_django_urls_for_package(package_name: str):
7-
ModuleDiscovery().import_recursively(package_name)
7+
import_recursively(package_name)
88
uripatterns = []
9-
for class_name, cls in ModuleDiscovery().get_all_classes(package_name):
9+
for class_name, cls in get_all_classes(package_name):
1010
uripatterns += winter_django.create_django_urls(cls)
1111
return uripatterns

0 commit comments

Comments
 (0)