simple-module-settings 0.0.1__py3-none-any.whl

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.
settings/services.py ADDED
@@ -0,0 +1,36 @@
1
+ """Module-scoped state container.
2
+
3
+ Stored as ``app.state.settings`` by
4
+ :meth:`SettingsModule.register_settings`.
5
+
6
+ Not frozen — ``on_startup`` may set fields that depend on the DB or
7
+ other framework services. Convention: set once during boot, treat as
8
+ read-only after.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+
15
+ from settings.contracts.registry import SettingsRegistry
16
+ from settings.module_registry import ModuleSettingsRegistry
17
+ from settings.settings import SettingsSettings
18
+
19
+
20
+ @dataclass
21
+ class SettingsServices:
22
+ """Settings module singletons.
23
+
24
+ ``registry`` is shared across the whole app — consumer modules register
25
+ their setting keys here (typically from their own ``on_startup`` hook)
26
+ so admins can discover every knob and the accessor can fall back to
27
+ declared defaults.
28
+
29
+ ``module_registry`` tracks per-module ``BaseSettings`` classes registered
30
+ via ``register_module_settings``; the hosting lifespan uses it to hydrate
31
+ each module's effective settings from the DB before ``on_startup`` runs.
32
+ """
33
+
34
+ settings: SettingsSettings
35
+ registry: SettingsRegistry = field(default_factory=SettingsRegistry)
36
+ module_registry: ModuleSettingsRegistry = field(default_factory=ModuleSettingsRegistry)
settings/settings.py ADDED
@@ -0,0 +1,11 @@
1
+ """Settings module's own configuration (DB-backed)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic_settings import BaseSettings, SettingsConfigDict
6
+
7
+
8
+ class SettingsSettings(BaseSettings):
9
+ """Configuration for the Settings module."""
10
+
11
+ model_config = SettingsConfigDict(extra="ignore")
settings/store.py ADDED
@@ -0,0 +1,61 @@
1
+ """DB-backed key/value store for module settings (SYSTEM scope).
2
+
3
+ Wraps the existing SettingService. Keys are namespaced ``<package>.<field>``
4
+ to avoid collision with free-form user-defined setting keys.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from settings.constants import SYSTEM_SCOPE_ID
10
+ from settings.contracts.schemas import SettingScope, SettingUpsert, SettingValueType
11
+ from settings.service import SettingService
12
+
13
+
14
+ def _key(package: str, field: str) -> str:
15
+ return f"{package}.{field}"
16
+
17
+
18
+ class SettingsStore:
19
+ """SYSTEM-scoped key/value store keyed by ``(package, field)``."""
20
+
21
+ def __init__(self, service: SettingService) -> None:
22
+ self._service = service
23
+
24
+ async def get_overrides(self, package: str) -> dict[str, tuple[str, str]]:
25
+ """Return ``{field_name: (raw_value, value_type)}`` for a package."""
26
+ prefix = f"{package}."
27
+ items = await self._service.list_by_scope(SettingScope.SYSTEM, SYSTEM_SCOPE_ID)
28
+ out: dict[str, tuple[str, str]] = {}
29
+ for item in items:
30
+ if not item.key.startswith(prefix):
31
+ continue
32
+ field_name = item.key[len(prefix) :]
33
+ if "." in field_name:
34
+ continue
35
+ out[field_name] = (item.value, item.value_type)
36
+ return out
37
+
38
+ async def set_override(self, package: str, field: str, value: str, value_type: str) -> None:
39
+ await self._service.upsert_scoped(
40
+ SettingScope.SYSTEM,
41
+ SYSTEM_SCOPE_ID,
42
+ _key(package, field),
43
+ SettingUpsert(value=value, value_type=SettingValueType(value_type)),
44
+ )
45
+
46
+ async def clear_override(self, package: str, field: str) -> None:
47
+ await self._service.delete_scoped(
48
+ SettingScope.SYSTEM, SYSTEM_SCOPE_ID, _key(package, field)
49
+ )
50
+
51
+ async def list_packages_with_overrides(self) -> list[str]:
52
+ items = await self._service.list_by_scope(SettingScope.SYSTEM, SYSTEM_SCOPE_ID)
53
+ pkgs: set[str] = set()
54
+ for item in items:
55
+ if "." not in item.key:
56
+ continue
57
+ pkg, rest = item.key.split(".", 1)
58
+ if "." in rest:
59
+ continue
60
+ pkgs.add(pkg)
61
+ return sorted(pkgs)
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: simple_module_settings
3
+ Version: 0.0.1
4
+ Summary: Runtime settings UI — modules plug their own settings panels into a shared admin view
5
+ Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
+ Project-URL: Repository, https://github.com/antosubash/simple_module_python
7
+ Project-URL: Issues, https://github.com/antosubash/simple_module_python/issues
8
+ Project-URL: Changelog, https://github.com/antosubash/simple_module_python/blob/main/CHANGELOG.md
9
+ Author-email: Anto Subash <antosubash@live.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: admin,configuration,settings,simple-module
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Framework :: FastAPI
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.12
24
+ Requires-Dist: simple-module-core==0.0.1
25
+ Requires-Dist: simple-module-db==0.0.1
26
+ Requires-Dist: simple-module-hosting==0.0.1
27
+ Description-Content-Type: text/markdown
28
+
29
+ # simple_module_settings
30
+
31
+ Runtime settings UI for [simple_module](https://github.com/antosubash/simple_module_python) apps. Other modules plug their own settings panels into a shared admin view — one page per module tab — without the host having to know about them.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install simple_module_settings
37
+ ```
38
+
39
+ ## What it provides
40
+
41
+ - `/settings` admin page aggregating every installed module's settings panel.
42
+ - `register_settings_panel()` hook — a module declares `{title, inertia_page, requires_permission}`; `simple_module_settings` renders the tab.
43
+ - DB-backed runtime settings table (separate from env-var-driven `SM_*` settings) for values admins change at runtime.
44
+
45
+ ## Usage
46
+
47
+ Register a panel:
48
+
49
+ ```python
50
+ class OrdersModule(ModuleBase):
51
+ meta = ModuleMeta(name="orders")
52
+
53
+ def register_settings_panel(self):
54
+ return {
55
+ "title": "Orders",
56
+ "inertia_page": "Orders/SettingsPanel",
57
+ "requires_permission": "orders.manage",
58
+ }
59
+ ```
60
+
61
+ That adds an **Orders** tab at `/settings`. The rendered page is a regular Inertia page authored inside the `orders` module.
62
+
63
+ ## Depends on
64
+
65
+ - `simple_module_core`, `simple_module_db`, `simple_module_hosting`
66
+
67
+ ## License
68
+
69
+ MIT — see [LICENSE](https://github.com/antosubash/simple_module_python/blob/main/LICENSE).
@@ -0,0 +1,41 @@
1
+ settings/__init__.py,sha256=QvBSBkRsWCrl_h6TbbceY8wsG3VHa-rZyHkBJAK3DBk,23
2
+ settings/_module_settings.py,sha256=feKyRbLDHipLQtqnI11JYwWygNFrGG4ArBjh06JpyL4,5501
3
+ settings/cli.py,sha256=KWKYoDzrv8tLuxs-cx0hnD8HEnfRN4pKVTIVOfZQdZo,2475
4
+ settings/constants.py,sha256=OjaY4YjQPn_ukPMpUR9szwmfQNGcxfNYoUtbU2Ij_uk,5784
5
+ settings/deps.py,sha256=b-VNFTIa_6wC8SOXnGyRy9MmrRVdAgRPFz1sYtNqzvY,2134
6
+ settings/env_vars.py,sha256=_AP8Sxd_5Zg_zCip4h6ATaHIY-87Fu3mv1jMk0yKKXo,578
7
+ settings/hydrate.py,sha256=z1TVrohDIkmz5deUsYOqDwaJqNu01MPCAIA9reRjm2M,2009
8
+ settings/models.py,sha256=6agZmTGZB8wa40xzqYUX075W1V8Kh2VN5u-iG_z13U4,1875
9
+ settings/module.py,sha256=U3eM0laD_zaye1rcQMBPXLpfxGm7U5qo8rsScEmWv8I,2448
10
+ settings/module_registry.py,sha256=fKkn0s1Qfwb5oC5hoZtY6JnSuFcidhgdGOlMD9La3rk,1073
11
+ settings/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ settings/registration.py,sha256=qgclQBC3U4oBUqASW8rypNB5BwzVBUBZh5rewcCwSnk,1127
13
+ settings/reload.py,sha256=XqS8ejXvtJguk17sOVXxUiFVbnqZfMZnHiJ3aUNcAO8,1786
14
+ settings/service.py,sha256=euw39CKZyHFszkrddYauQrlLiml9T4K75NLtiBScoG0,6289
15
+ settings/services.py,sha256=x0GcwYVqGFonRc0O4lQL8maocJdWXFZp4NO2BokJgYw,1277
16
+ settings/settings.py,sha256=0fauk-Jjt3EuPWTMeoNJSlCBmukw__1mu0y4GQ5UeVg,299
17
+ settings/store.py,sha256=MlLd7V3gWHgrTOKirEQXZbr9qngQ3q67UqA77fmyqm4,2239
18
+ settings/contracts/__init__.py,sha256=YcvVFbGmMiG3rNZ7ucXrr2m1YNDiNmBSUzPRHOfAVhs,568
19
+ settings/contracts/accessor.py,sha256=1GdPkY0nWSdSARRfkwVvKtKHbnGdDgP-IjtgWeB2gTI,7664
20
+ settings/contracts/events.py,sha256=Ii7TPTeX-l3Wlxhl8BasW1sdMNZA_7t3H_oXD2ilb4o,510
21
+ settings/contracts/registry.py,sha256=IRGZs33FMn63DbQ7A2Ryih5eVeG4YjIMewiMb2Zkhcw,2350
22
+ settings/contracts/schemas.py,sha256=182JfnRWmmIPrHreQU2cR0x4CvxuvIWFI5FDYGQB_tU,4834
23
+ settings/endpoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ settings/endpoints/api.py,sha256=f_5mnMrH5fxGnYz_YK0KcQaFxHawQr3vNq01GuGTmBA,6211
25
+ settings/endpoints/module_api.py,sha256=bRehranGhNy35ETKt8EaJomqU-sJYHdE1v2GodandvE,3634
26
+ settings/endpoints/views.py,sha256=ESoU9BRbiPu7Ugty7TvC_o-u1jzejV7Zkeue7S6AJ9Q,4143
27
+ settings/locales/en.json,sha256=2lUi1IOpMerqjCqfagABj7RktCdMgYfoKzf-ac75erQ,1981
28
+ settings/pages/Browse.tsx,sha256=TrUkjfSrpmsSRHDl3E863ULEeFhw-w5GuStB5KgoDLg,4054
29
+ settings/pages/Create.tsx,sha256=3agamQsnTZSqzShKWdrhh3zKJ_viaKTurcMDCiw1hqE,4262
30
+ settings/pages/Edit.tsx,sha256=dMxsWkyIYs3FRi0FG0KBEfAZJ6EUvVBQoVPYJZPMcNY,3433
31
+ settings/pages/ModulesEdit.tsx,sha256=j2gW6mcAyypplMhdlrg-dthFujZ3PF-KSAxxYSh84Dc,2619
32
+ settings/pages/routes.ts,sha256=Az-ZyapYL0ERhMIJ691TCFF9UspYPJfDKvpyzEawpaE,212
33
+ settings/pages/components/FieldInput.tsx,sha256=dYKY2kIIHLprjmPCSGngKUa_vRuihZlTHftEJAW6HRg,2431
34
+ settings/pages/components/ModuleForm.tsx,sha256=V9tr1NKhMUGbP9qYaYMKj5z6xx4fiPQo_qzOQkSVroA,5247
35
+ settings/pages/components/ValueInput.tsx,sha256=DVA53bcwiBPssORlJLx5y--NYdlvXK3-92403UQtqAM,2207
36
+ settings/package.json,sha256=sRi7iWCtnjrg6W8jzGobPwq-2gduRSbq--RiKw6JQ9g,379
37
+ simple_module_settings-0.0.1.dist-info/METADATA,sha256=emXVxIpniaCN0AVn5SYGK1-nPkplCTzsDMz1UUNKKKk,2620
38
+ simple_module_settings-0.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
39
+ simple_module_settings-0.0.1.dist-info/entry_points.txt,sha256=WNiRkLdOVTEDrNfawkYO8q2m2wb52W6LJNp0nfn9vI4,109
40
+ simple_module_settings-0.0.1.dist-info/licenses/LICENSE,sha256=Yn66lhLklsF5p7pa85_ksQrJ79Q-FgOaUAHevLBjer4,1068
41
+ simple_module_settings-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ sm-settings = settings.cli:main
3
+
4
+ [simple_module]
5
+ settings = settings.module:SettingsModule
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anto Subash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.