simple-module-core 0.0.28__tar.gz → 0.0.30__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/PKG-INFO +1 -1
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/pyproject.toml +1 -1
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/__init__.py +6 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/__main__.py +10 -1
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/discovery.py +65 -0
- simple_module_core-0.0.30/tests/test_auth_provider_selection.py +133 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/.gitignore +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/LICENSE +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/README.md +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/design_packs.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/__init__.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_coupling.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_css.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_i18n.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_inertia_api.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_js_workspace.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_migration.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_module.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_pages.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_runner.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_types.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/dotenv.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/environments.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/events.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/exceptions.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/feature_flags.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/health.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/i18n.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/menu.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/module.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/permissions.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/public_routes.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/py.typed +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/services.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/versioning.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_css_diagnostics.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_css_scanner.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_design_packs.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_diagnostics.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_discovery.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_dotenv.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_environments.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_events.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_feature_flags.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_feature_flags_decorator.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_health_registry.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_i18n.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_i18n_diagnostics.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_menu.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_module_base.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_module_diagnostics.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_permissions.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_public_routes.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_services.py +0 -0
- {simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_versioning.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simple_module_core
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.30
|
|
4
4
|
Summary: Module-system primitives for the simple_module framework — ModuleBase, discovery, diagnostics, events
|
|
5
5
|
Project-URL: Homepage, https://github.com/antosubash/simple_module_python
|
|
6
6
|
Project-URL: Repository, https://github.com/antosubash/simple_module_python
|
|
@@ -8,8 +8,11 @@ from simple_module_core.diagnostics import (
|
|
|
8
8
|
run_diagnostics,
|
|
9
9
|
)
|
|
10
10
|
from simple_module_core.discovery import (
|
|
11
|
+
DEFAULT_AUTH_PROVIDER,
|
|
11
12
|
discover_modules,
|
|
12
13
|
get_module_package_name,
|
|
14
|
+
resolve_auth_provider,
|
|
15
|
+
select_auth_provider,
|
|
13
16
|
topological_sort,
|
|
14
17
|
)
|
|
15
18
|
from simple_module_core.events import Event, EventBus
|
|
@@ -39,6 +42,7 @@ from simple_module_core.services import Services
|
|
|
39
42
|
from simple_module_core.versioning import FRAMEWORK_API_VERSION, check_framework_compatibility
|
|
40
43
|
|
|
41
44
|
__all__ = [
|
|
45
|
+
"DEFAULT_AUTH_PROVIDER",
|
|
42
46
|
"FRAMEWORK_API_VERSION",
|
|
43
47
|
"CircularDependencyError",
|
|
44
48
|
"DesignPack",
|
|
@@ -77,6 +81,8 @@ __all__ = [
|
|
|
77
81
|
"is_flag_enabled",
|
|
78
82
|
"print_diagnostics",
|
|
79
83
|
"require_flag",
|
|
84
|
+
"resolve_auth_provider",
|
|
80
85
|
"run_diagnostics",
|
|
86
|
+
"select_auth_provider",
|
|
81
87
|
"topological_sort",
|
|
82
88
|
]
|
|
@@ -25,7 +25,12 @@ from simple_module_core.diagnostics import (
|
|
|
25
25
|
print_diagnostics,
|
|
26
26
|
run_diagnostics,
|
|
27
27
|
)
|
|
28
|
-
from simple_module_core.discovery import
|
|
28
|
+
from simple_module_core.discovery import (
|
|
29
|
+
discover_modules,
|
|
30
|
+
resolve_auth_provider,
|
|
31
|
+
select_auth_provider,
|
|
32
|
+
topological_sort,
|
|
33
|
+
)
|
|
29
34
|
from simple_module_core.dotenv import parse_dotenv
|
|
30
35
|
from simple_module_core.exceptions import InvalidModuleError
|
|
31
36
|
|
|
@@ -100,6 +105,10 @@ def main() -> int:
|
|
|
100
105
|
print("No modules discovered. Is the project installed (`uv sync --all-packages`)?")
|
|
101
106
|
return 0
|
|
102
107
|
|
|
108
|
+
# Mirror the host: only the configured auth provider is active, so doctor
|
|
109
|
+
# reports on the same module set the app actually boots with.
|
|
110
|
+
modules = select_auth_provider(modules, resolve_auth_provider())
|
|
111
|
+
|
|
103
112
|
# Topological sort surfaces CircularDependencyError early.
|
|
104
113
|
modules = topological_sort(modules)
|
|
105
114
|
|
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
from collections.abc import Sequence
|
|
7
7
|
from importlib.metadata import entry_points
|
|
8
8
|
|
|
9
|
+
from simple_module_core.dotenv import env_str, load_dotenv_into_environ
|
|
9
10
|
from simple_module_core.exceptions import CircularDependencyError, InvalidModuleError
|
|
10
11
|
from simple_module_core.module import ModuleBase, ModuleMeta
|
|
11
12
|
|
|
@@ -13,6 +14,9 @@ logger = logging.getLogger(__name__)
|
|
|
13
14
|
|
|
14
15
|
ENTRY_POINT_GROUP = "simple_module"
|
|
15
16
|
|
|
17
|
+
DEFAULT_AUTH_PROVIDER = "users"
|
|
18
|
+
"""Auth provider activated when several are installed — see select_auth_provider."""
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
def get_module_package_name(module: ModuleBase) -> str:
|
|
18
22
|
"""Return the top-level Python package a module instance belongs to.
|
|
@@ -121,6 +125,67 @@ def discover_modules(
|
|
|
121
125
|
return modules
|
|
122
126
|
|
|
123
127
|
|
|
128
|
+
def resolve_auth_provider() -> str:
|
|
129
|
+
"""Return the configured auth provider name, honoring ``.env``.
|
|
130
|
+
|
|
131
|
+
For tools that run outside the host process (``make doctor``, ``smpy host
|
|
132
|
+
gen-pages``) and so have no ``BootstrapSettings`` to read from. The host
|
|
133
|
+
passes ``settings.auth_provider`` to :func:`select_auth_provider` instead.
|
|
134
|
+
"""
|
|
135
|
+
load_dotenv_into_environ()
|
|
136
|
+
return env_str("SM_AUTH_PROVIDER", DEFAULT_AUTH_PROVIDER)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def select_auth_provider(
|
|
140
|
+
modules: Sequence[ModuleBase],
|
|
141
|
+
preferred: str = DEFAULT_AUTH_PROVIDER,
|
|
142
|
+
*,
|
|
143
|
+
strict: bool = False,
|
|
144
|
+
) -> list[ModuleBase]:
|
|
145
|
+
"""Drop every auth provider except ``preferred`` when several are installed.
|
|
146
|
+
|
|
147
|
+
``users`` and ``keycloak`` both claim ``app.state.auth.auth_provider``, so
|
|
148
|
+
only one can be active — that's what SM020 reports. A dev workspace has
|
|
149
|
+
both installed (``uv sync --all-packages`` installs every member), which
|
|
150
|
+
would otherwise fail the boot on a fresh clone. Selecting one here keeps
|
|
151
|
+
the alternative provider installed and testable but inert.
|
|
152
|
+
|
|
153
|
+
Nothing is dropped when fewer than two providers are present — a host that
|
|
154
|
+
installs only ``keycloak`` keeps it whatever ``preferred`` says.
|
|
155
|
+
|
|
156
|
+
A ``preferred`` that names none of the installed providers is a
|
|
157
|
+
misconfiguration (typically a typo). Leaving every provider mounted means
|
|
158
|
+
two modules write ``app.state.auth.auth_provider`` and the topological
|
|
159
|
+
order silently decides the winner, so this always warns, and with
|
|
160
|
+
``strict`` raises :class:`InvalidModuleError`. Callers in production
|
|
161
|
+
should pass ``strict=True``: SM020 would catch this, but diagnostics run
|
|
162
|
+
in development only, so nothing else reports it in a deployed app.
|
|
163
|
+
"""
|
|
164
|
+
providers = [m for m in modules if getattr(m, "_is_auth_provider", False)]
|
|
165
|
+
if len(providers) < 2:
|
|
166
|
+
return list(modules)
|
|
167
|
+
|
|
168
|
+
installed = ", ".join(m.meta.name for m in providers)
|
|
169
|
+
keep = next((m for m in providers if m.meta.name.lower() == preferred.lower()), None)
|
|
170
|
+
if keep is None:
|
|
171
|
+
msg = (
|
|
172
|
+
f"SM_AUTH_PROVIDER={preferred!r} matches none of the installed auth "
|
|
173
|
+
f"providers ({installed}); all of them stay mounted and the last one "
|
|
174
|
+
"registered wins"
|
|
175
|
+
)
|
|
176
|
+
if strict:
|
|
177
|
+
raise InvalidModuleError(msg)
|
|
178
|
+
logger.warning("%s — set it to one of the names above", msg)
|
|
179
|
+
return list(modules)
|
|
180
|
+
|
|
181
|
+
logger.info(
|
|
182
|
+
"Auth provider '%s' active (SM_AUTH_PROVIDER); skipping also-installed: %s",
|
|
183
|
+
keep.meta.name,
|
|
184
|
+
", ".join(m.meta.name for m in providers if m is not keep),
|
|
185
|
+
)
|
|
186
|
+
return [m for m in modules if m is keep or not getattr(m, "_is_auth_provider", False)]
|
|
187
|
+
|
|
188
|
+
|
|
124
189
|
def topological_sort(modules: Sequence[ModuleBase]) -> list[ModuleBase]:
|
|
125
190
|
"""Sort modules so dependencies come before dependents.
|
|
126
191
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Tests for select_auth_provider — picking one of several installed providers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from simple_module_core.discovery import (
|
|
10
|
+
DEFAULT_AUTH_PROVIDER,
|
|
11
|
+
resolve_auth_provider,
|
|
12
|
+
select_auth_provider,
|
|
13
|
+
)
|
|
14
|
+
from simple_module_core.exceptions import InvalidModuleError
|
|
15
|
+
from simple_module_core.module import ModuleBase, ModuleMeta
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FakeUsers(ModuleBase):
|
|
19
|
+
meta = ModuleMeta(name="Users")
|
|
20
|
+
_is_auth_provider = True
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FakeKeycloak(ModuleBase):
|
|
24
|
+
meta = ModuleMeta(name="Keycloak")
|
|
25
|
+
_is_auth_provider = True
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FakeDashboard(ModuleBase):
|
|
29
|
+
meta = ModuleMeta(name="Dashboard")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _names(modules):
|
|
33
|
+
return [m.meta.name for m in modules]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TestSelectAuthProvider:
|
|
37
|
+
def test_default_keeps_users_and_drops_keycloak(self):
|
|
38
|
+
"""Both installed (the dev workspace) → the default provider wins, no SM020."""
|
|
39
|
+
result = select_auth_provider([FakeUsers(), FakeKeycloak(), FakeDashboard()])
|
|
40
|
+
assert _names(result) == ["Users", "Dashboard"]
|
|
41
|
+
|
|
42
|
+
def test_preferred_keycloak_drops_users(self):
|
|
43
|
+
result = select_auth_provider([FakeUsers(), FakeKeycloak()], "keycloak")
|
|
44
|
+
assert _names(result) == ["Keycloak"]
|
|
45
|
+
|
|
46
|
+
def test_match_is_case_insensitive(self):
|
|
47
|
+
result = select_auth_provider([FakeUsers(), FakeKeycloak()], "KeyCloak")
|
|
48
|
+
assert _names(result) == ["Keycloak"]
|
|
49
|
+
|
|
50
|
+
def test_non_provider_modules_keep_their_order(self):
|
|
51
|
+
modules = [FakeDashboard(), FakeKeycloak(), FakeUsers()]
|
|
52
|
+
assert _names(select_auth_provider(modules)) == ["Dashboard", "Users"]
|
|
53
|
+
|
|
54
|
+
def test_lone_provider_survives_a_mismatched_preference(self):
|
|
55
|
+
"""A keycloak-only host keeps keycloak even at the default preference."""
|
|
56
|
+
result = select_auth_provider([FakeKeycloak(), FakeDashboard()], "users")
|
|
57
|
+
assert _names(result) == ["Keycloak", "Dashboard"]
|
|
58
|
+
|
|
59
|
+
def test_no_providers_is_untouched(self):
|
|
60
|
+
"""SM021 territory — nothing to select, and nothing to drop."""
|
|
61
|
+
assert _names(select_auth_provider([FakeDashboard()])) == ["Dashboard"]
|
|
62
|
+
|
|
63
|
+
def test_unknown_preference_keeps_every_provider(self):
|
|
64
|
+
"""Naming a provider that isn't installed must not silently pick one."""
|
|
65
|
+
result = select_auth_provider([FakeUsers(), FakeKeycloak()], "oidc")
|
|
66
|
+
assert _names(result) == ["Users", "Keycloak"]
|
|
67
|
+
|
|
68
|
+
def test_unknown_preference_warns(self, caplog):
|
|
69
|
+
"""SM020 only runs in development — a typo must not pass in silence."""
|
|
70
|
+
with caplog.at_level(logging.WARNING, logger="simple_module_core.discovery"):
|
|
71
|
+
select_auth_provider([FakeUsers(), FakeKeycloak()], "keycloack")
|
|
72
|
+
warnings = [r for r in caplog.records if r.levelno >= logging.WARNING]
|
|
73
|
+
assert warnings, "an unrecognised provider name logged nothing"
|
|
74
|
+
assert "keycloack" in warnings[0].getMessage()
|
|
75
|
+
|
|
76
|
+
def test_unknown_preference_raises_when_strict(self):
|
|
77
|
+
"""Production boots strict, where mounting both providers is not viable."""
|
|
78
|
+
with pytest.raises(InvalidModuleError, match="keycloack"):
|
|
79
|
+
select_auth_provider([FakeUsers(), FakeKeycloak()], "keycloack", strict=True)
|
|
80
|
+
|
|
81
|
+
def test_strict_is_quiet_on_a_valid_selection(self):
|
|
82
|
+
result = select_auth_provider([FakeUsers(), FakeKeycloak()], "users", strict=True)
|
|
83
|
+
assert _names(result) == ["Users"]
|
|
84
|
+
|
|
85
|
+
def test_strict_ignores_a_mismatch_when_only_one_is_installed(self):
|
|
86
|
+
"""Nothing is ambiguous with a single provider, so strict must not raise."""
|
|
87
|
+
result = select_auth_provider([FakeKeycloak()], "oidc", strict=True)
|
|
88
|
+
assert _names(result) == ["Keycloak"]
|
|
89
|
+
|
|
90
|
+
def test_selection_is_logged(self, caplog):
|
|
91
|
+
with caplog.at_level(logging.INFO, logger="simple_module_core.discovery"):
|
|
92
|
+
select_auth_provider([FakeUsers(), FakeKeycloak()])
|
|
93
|
+
assert any("Keycloak" in rec.getMessage() for rec in caplog.records)
|
|
94
|
+
|
|
95
|
+
def test_input_is_not_mutated(self):
|
|
96
|
+
modules = [FakeUsers(), FakeKeycloak()]
|
|
97
|
+
select_auth_provider(modules)
|
|
98
|
+
assert len(modules) == 2
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TestResolveAuthProvider:
|
|
102
|
+
@pytest.fixture(autouse=True)
|
|
103
|
+
def _isolated_environ(self, monkeypatch, tmp_path):
|
|
104
|
+
"""Swap in a throwaway ``os.environ``.
|
|
105
|
+
|
|
106
|
+
``resolve_auth_provider`` merges ``.env`` into the real environment via
|
|
107
|
+
``setdefault``, which ``monkeypatch.delenv(raising=False)`` can't undo —
|
|
108
|
+
it records nothing for a key that was already absent. Without this the
|
|
109
|
+
provider chosen here would leak into every later test in the session.
|
|
110
|
+
"""
|
|
111
|
+
monkeypatch.setattr(os, "environ", dict(os.environ))
|
|
112
|
+
monkeypatch.setenv("SM_PROJECT_ROOT", str(tmp_path))
|
|
113
|
+
monkeypatch.delenv("SM_AUTH_PROVIDER", raising=False)
|
|
114
|
+
|
|
115
|
+
def test_defaults_to_users(self):
|
|
116
|
+
assert resolve_auth_provider() == DEFAULT_AUTH_PROVIDER
|
|
117
|
+
|
|
118
|
+
def test_reads_the_env_var(self, monkeypatch):
|
|
119
|
+
monkeypatch.setenv("SM_AUTH_PROVIDER", "keycloak")
|
|
120
|
+
assert resolve_auth_provider() == "keycloak"
|
|
121
|
+
|
|
122
|
+
def test_blank_falls_back_to_the_default(self, monkeypatch):
|
|
123
|
+
monkeypatch.setenv("SM_AUTH_PROVIDER", " ")
|
|
124
|
+
assert resolve_auth_provider() == DEFAULT_AUTH_PROVIDER
|
|
125
|
+
|
|
126
|
+
def test_reads_dotenv_when_env_is_unset(self, tmp_path):
|
|
127
|
+
(tmp_path / ".env").write_text("SM_AUTH_PROVIDER=keycloak\n", encoding="utf-8")
|
|
128
|
+
assert resolve_auth_provider() == "keycloak"
|
|
129
|
+
|
|
130
|
+
def test_real_environment_wins_over_dotenv(self, monkeypatch, tmp_path):
|
|
131
|
+
(tmp_path / ".env").write_text("SM_AUTH_PROVIDER=keycloak\n", encoding="utf-8")
|
|
132
|
+
monkeypatch.setenv("SM_AUTH_PROVIDER", "users")
|
|
133
|
+
assert resolve_auth_provider() == "users"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/__init__.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_coupling.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_css.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_i18n.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_migration.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_module.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_pages.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_runner.py
RENAMED
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/simple_module_core/diagnostics/_types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_module_core-0.0.28 → simple_module_core-0.0.30}/tests/test_feature_flags_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|