apprc 0.1.0__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.
Files changed (54) hide show
  1. apprc-0.1.0/LICENSE +21 -0
  2. apprc-0.1.0/PKG-INFO +498 -0
  3. apprc-0.1.0/README.pypi.md +468 -0
  4. apprc-0.1.0/pyproject.toml +190 -0
  5. apprc-0.1.0/src/apprc/__init__.py +19 -0
  6. apprc-0.1.0/src/apprc/_dotenv_guard.py +29 -0
  7. apprc-0.1.0/src/apprc/_lazy.py +62 -0
  8. apprc-0.1.0/src/apprc/cli/__init__.py +31 -0
  9. apprc-0.1.0/src/apprc/cli/bootstrap.py +66 -0
  10. apprc-0.1.0/src/apprc/cli/config_app.py +555 -0
  11. apprc-0.1.0/src/apprc/cli/doctor.py +182 -0
  12. apprc-0.1.0/src/apprc/cli/options.py +17 -0
  13. apprc-0.1.0/src/apprc/cli/typer_utils.py +153 -0
  14. apprc-0.1.0/src/apprc/config/__init__.py +49 -0
  15. apprc-0.1.0/src/apprc/config/app_spec.py +69 -0
  16. apprc-0.1.0/src/apprc/config/base_config.py +429 -0
  17. apprc-0.1.0/src/apprc/config/environment.py +329 -0
  18. apprc-0.1.0/src/apprc/config/kit.py +299 -0
  19. apprc-0.1.0/src/apprc/config/local_env.py +171 -0
  20. apprc-0.1.0/src/apprc/config/schema.py +396 -0
  21. apprc-0.1.0/src/apprc/config/storage_registry.py +268 -0
  22. apprc-0.1.0/src/apprc/config/tui.py +452 -0
  23. apprc-0.1.0/src/apprc/config/tui_rendering.py +225 -0
  24. apprc-0.1.0/src/apprc/logging/__init__.py +5 -0
  25. apprc-0.1.0/src/apprc/logging/__init__.pyi +27 -0
  26. apprc-0.1.0/src/apprc/logging/_facade.py +44 -0
  27. apprc-0.1.0/src/apprc/logging/config.py +239 -0
  28. apprc-0.1.0/src/apprc/logging/context.py +168 -0
  29. apprc-0.1.0/src/apprc/logging/core.py +633 -0
  30. apprc-0.1.0/src/apprc/logging/exceptions.py +383 -0
  31. apprc-0.1.0/src/apprc/logging/formats.py +426 -0
  32. apprc-0.1.0/src/apprc/logging/functions/__init__.py +19 -0
  33. apprc-0.1.0/src/apprc/logging/functions/lifecycle.py +59 -0
  34. apprc-0.1.0/src/apprc/logging/functions/telemetry.py +326 -0
  35. apprc-0.1.0/src/apprc/logging/levels.py +192 -0
  36. apprc-0.1.0/src/apprc/logging/subprocess.py +64 -0
  37. apprc-0.1.0/src/apprc/main.py +28 -0
  38. apprc-0.1.0/src/apprc/paths.py +29 -0
  39. apprc-0.1.0/src/apprc/py.typed +1 -0
  40. apprc-0.1.0/src/apprc/utils/__init__.py +18 -0
  41. apprc-0.1.0/src/apprc/utils/path_resolver.py +206 -0
  42. apprc-0.1.0/src/apprc/utils/stdlib.py +91 -0
  43. apprc-0.1.0/src/apprc_dev/build/pypi_readme.py +102 -0
  44. apprc-0.1.0/tests/__init__.py +1 -0
  45. apprc-0.1.0/tests/support_config.py +137 -0
  46. apprc-0.1.0/tests/test_base_config.py +58 -0
  47. apprc-0.1.0/tests/test_config_kit.py +445 -0
  48. apprc-0.1.0/tests/test_environment_bootstrap.py +137 -0
  49. apprc-0.1.0/tests/test_local_env.py +97 -0
  50. apprc-0.1.0/tests/test_pypi_readme.py +43 -0
  51. apprc-0.1.0/tests/test_storage_registry.py +90 -0
  52. apprc-0.1.0/tests/test_tui_rendering.py +54 -0
  53. apprc-0.1.0/tests/test_typer_utils.py +54 -0
  54. apprc-0.1.0/tests/test_utils.py +21 -0
apprc-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 markur4
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.
apprc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,498 @@
1
+ Metadata-Version: 2.4
2
+ Name: apprc
3
+ Version: 0.1.0
4
+ Summary: Reusable runtime configuration, config CLI, and logging helpers for Python applications.
5
+ Keywords: configuration,dotenv,logging,runtime-config,typer
6
+ Author: markur4
7
+ Author-email: markur4 <noreply@hisqu.de>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Dist: python-dotenv
20
+ Requires-Dist: typed-settings[dotenv]
21
+ Requires-Dist: structlog
22
+ Requires-Dist: rich
23
+ Requires-Dist: textual
24
+ Requires-Dist: typer
25
+ Requires-Python: >=3.12
26
+ Project-URL: Homepage, https://hisqu.de
27
+ Project-URL: Repository, https://github.com/HisQu/apprc
28
+ Project-URL: Issues, https://github.com/HisQu/apprc/issues
29
+ Description-Content-Type: text/markdown
30
+
31
+ <!-- ============================================================== -->
32
+ <!-- == Header ==================================================== -->
33
+ <div align="center">
34
+
35
+ # `apprc`: Application Runtime Config
36
+
37
+ *Part of:*
38
+
39
+ <a href="https://hisqu.de" target="_blank">
40
+ <img
41
+ src="https://avatars.githubusercontent.com/u/196629600?s=200&v=4"
42
+ width="100px" alt="logo"
43
+ style="margin-top: -10px;">
44
+ </a>
45
+
46
+ <br>
47
+
48
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
49
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
50
+
51
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
52
+ [![Pyright](https://img.shields.io/badge/type%20checked-pyright-blue)](https://microsoft.github.io/pyright/)
53
+ [![pytest](https://img.shields.io/badge/tested%20with-pytest-0A9EDC)](https://docs.pytest.org/)
54
+
55
+ </div>
56
+
57
+ [`direnv`]: https://direnv.net/
58
+ [`just`]: https://github.com/casey/just?tab=readme-ov-file#packages
59
+ [`uv`]: https://github.com/astral-sh/uv?tab=readme-ov-file#uv
60
+ [PEP 621]: https://peps.python.org/pep-0621/#packaging-type-information
61
+ [`typed-settings`]: https://typed-settings.readthedocs.io/
62
+ [`typer`]: https://typer.tiangolo.com/
63
+ [`python-dotenv`]: https://github.com/theskumar/python-dotenv
64
+ [`textual`]: https://github.com/Textualize/textual
65
+ [`structlog`]: https://github.com/hynek/structlog
66
+
67
+
68
+ `apprc` is a small Python library for application runtime configuration and
69
+ logging. It is useful when a project needs typed env-backed config, packaged
70
+ defaults, per-user storage registries, local override files, a reusable
71
+ `config` CLI, a terminal config editor, and structured logs without rebuilding
72
+ that plumbing in every application.
73
+
74
+ The key idea: the application declares its config contract once, and `apprc`
75
+ derives the boring workflows from that contract.
76
+
77
+ ### Tech Stack:
78
+ - [`typed-settings`] for typed config fields and runtime dataclasses.
79
+ - [`typer`] for CLI commands.
80
+ - [`python-dotenv`] for `.env` file parsing and writing.
81
+ - [`textual`] for the terminal config editor.
82
+ - [`structlog`] for structured logging.
83
+
84
+ <br>
85
+
86
+ # Installation
87
+
88
+ **Important**
89
+
90
+ ### Prerequisites
91
+ - Python >=3.12
92
+ - `git`
93
+ - Optional: [`uv`], [`just`], [`direnv`]
94
+
95
+ <br>
96
+
97
+ ### Install From PyPI
98
+
99
+ ```shell
100
+ python -m pip install apprc
101
+ ```
102
+
103
+ <br>
104
+
105
+ ### Use `apprc` As Project Dependency
106
+
107
+ Any Python build system supporting `pyproject.toml` ([PEP 621]) works.
108
+
109
+ For published releases, depend on the PyPI package:
110
+
111
+ ```toml
112
+ [project]
113
+ dependencies = [
114
+ "apprc",
115
+ ]
116
+ ```
117
+
118
+ For the current repository revision, depend on the Git URL:
119
+
120
+ ```toml
121
+ [project]
122
+ dependencies = [
123
+ "apprc @ git+https://github.com/HisQu/apprc.git",
124
+ ]
125
+ ```
126
+
127
+ <br>
128
+
129
+ ### Editable Install With `pip`
130
+
131
+ ```shell
132
+ git clone https://github.com/HisQu/apprc.git
133
+ cd apprc
134
+
135
+ python -m venv .venv
136
+ source .venv/bin/activate
137
+ .venv/bin/python -m pip install --upgrade pip
138
+ .venv/bin/python -m pip install -e "." --group dev
139
+ ```
140
+
141
+ <br>
142
+
143
+ ### Editable Install With `uv`
144
+
145
+ ```shell
146
+ git clone https://github.com/HisQu/apprc.git
147
+ cd apprc
148
+
149
+ uv sync --frozen --all-groups
150
+ ```
151
+
152
+ <br>
153
+
154
+ ### Verify
155
+
156
+ ```shell
157
+ python -c "import apprc; print(apprc.AppConfigKit)"
158
+ pytest
159
+ ```
160
+
161
+ <br>
162
+
163
+ # Example
164
+
165
+ An application usually wires AppRC in three steps: declare fields, create the
166
+ kit, and mount the generated `config` CLI.
167
+
168
+ ```python
169
+ from dataclasses import dataclass
170
+
171
+ import typer
172
+
173
+ from apprc import AppConfigKit
174
+ from apprc.config import ConfigOwner, config_field
175
+
176
+ # 1) Declare the config fields your app owns.
177
+ CLIENT_OWNER = ConfigOwner(
178
+ key="client",
179
+ title="Client",
180
+ env_prefix="MYAPP_",
181
+ rc_path=("client",),
182
+ runtime_cls=None,
183
+ fields=(
184
+ config_field(
185
+ "model",
186
+ "MODEL",
187
+ str,
188
+ default="demo-model",
189
+ title="Model",
190
+ explanation="Model used by client calls.",
191
+ ),
192
+ ),
193
+ )
194
+
195
+ # 2) Create the reusable AppRC facade for your application.
196
+ MYAPP_CONFIG = AppConfigKit(
197
+ app_name="myapp",
198
+ display_name="MyApp",
199
+ config_package="myapp.config",
200
+ owners=(CLIENT_OWNER,),
201
+ storage_root_env_key="MYAPP_D_STORAGE",
202
+ registry_filename="myapp.toml",
203
+ shared_env_filename=".env.shared",
204
+ local_env_filename=".env.local",
205
+ )
206
+
207
+ # 3) Mount the generated `myapp config ...` command group.
208
+ @dataclass(slots=True)
209
+ class CliState:
210
+ env_bootstrap: object | None = None
211
+ storage: str | None = None
212
+
213
+
214
+ app = typer.Typer()
215
+ app.add_typer(
216
+ MYAPP_CONFIG.typer_app(state_type=CliState),
217
+ name="config",
218
+ )
219
+ ```
220
+
221
+ Runtime dataclasses can inherit `BaseEnv` when you want typed objects populated
222
+ from the bootstrapped environment. The important first step is the owner
223
+ inventory: AppRC reuses it for loading, validation, docs, CLI commands, and the
224
+ terminal editor.
225
+
226
+ AppRC does not install its own `apprc` command. The config CLI is meant to be
227
+ mounted as a subcommand of the application that depends on AppRC.
228
+
229
+ Users then get:
230
+
231
+ ```shell
232
+ myapp config init /absolute/path/to/storage --name default --default
233
+ myapp config doctor
234
+ myapp config show --json
235
+ myapp config set client.model other-model
236
+ myapp config edit
237
+ ```
238
+
239
+ <br>
240
+
241
+ # Explanations
242
+
243
+ ### Config Model
244
+
245
+ `apprc` separates config into clear layers:
246
+
247
+ | Layer | Owner | Purpose |
248
+ |---|---|---|
249
+ | `ConfigField` | application | One typed env-backed setting. |
250
+ | `ConfigOwner` | application | A named section of related fields. |
251
+ | `.env.shared` | application package | Packaged defaults shipped with code. |
252
+ | `<storage>/.env.local` | user/project | Per-storage local overrides. |
253
+ | shell environment | user/process | Highest-priority process values by default. |
254
+ | `~/.config/<app>/<app>.toml` | AppRC registry | Named storage roots and default storage. |
255
+
256
+ Runtime dataclasses inherit `BaseEnv`. The dataclass owns Python attributes;
257
+ `ConfigOwner` owns env names, docs labels, editor labels, choices, and
258
+ redaction metadata.
259
+
260
+ ### Bootstrap Precedence
261
+
262
+ Applications call `AppConfigKit.bootstrap(...)` once at CLI startup. It merges:
263
+
264
+ 1. packaged `.env.shared`
265
+ 2. selected storage-local `.env.local`
266
+ 3. explicit `--env-file`
267
+ 4. current shell environment
268
+
269
+ By default, the shell wins over `--env-file`. Set
270
+ `env_file_overrides_shell=True` when an explicit file should win inside the
271
+ current process.
272
+
273
+ ### Storage Registries
274
+
275
+ Globally installed commands need to find user data without hardcoding one path.
276
+ `apprc` stores named roots in `~/.config/<app>/<registry_filename>`, for
277
+ example:
278
+
279
+ ```toml
280
+ default_storage = "default"
281
+
282
+ [storages.default]
283
+ root = "/absolute/path/to/storage"
284
+ ```
285
+
286
+ Each storage root owns its own local override file, such as `.env.local`.
287
+
288
+ ### Logging
289
+
290
+ `apprc.logging` wraps stdlib logging and structlog. `setup_logging()` installs
291
+ formatters and dependency logger levels. `get_logger()` returns an `AppLogger`
292
+ with semantic helper methods such as `action_begin`, `success`, `retry`,
293
+ `fallback`, `telemetry`, and `traceback`.
294
+
295
+ Use AppRC logging when application logs should stay structured and readable in
296
+ CLI output, notebooks, and tests.
297
+
298
+ <br>
299
+
300
+ # Guides
301
+
302
+ ### Define Config Fields
303
+
304
+ Put config declarations in your application package, usually
305
+ `myapp/config/owners.py`.
306
+
307
+ ```python
308
+ from pathlib import Path
309
+
310
+ from apprc.config import CONFIG_MISSING, ConfigOwner, config_field
311
+
312
+ STORAGE_OWNER = ConfigOwner(
313
+ key="storage",
314
+ title="Storage",
315
+ env_prefix="MYAPP_",
316
+ rc_path=("storage",),
317
+ runtime_cls=None,
318
+ fields=(
319
+ config_field(
320
+ "root",
321
+ "D_STORAGE",
322
+ Path,
323
+ default=CONFIG_MISSING,
324
+ editable=False,
325
+ required=True,
326
+ explanation_short="Active storage root.",
327
+ explanation_long="Selected through the AppRC storage registry.",
328
+ ),
329
+ ),
330
+ )
331
+ ```
332
+
333
+ Use `editable=False` for values owned by the registry instead of `.env.local`.
334
+
335
+ ### Bootstrap a CLI
336
+
337
+ Call bootstrap before creating runtime config objects.
338
+
339
+ ```python
340
+ from apprc.cli import bootstrap_cli_env
341
+ from apprc.logging import setup_logging
342
+
343
+ state.env_bootstrap = bootstrap_cli_env(
344
+ MYAPP_CONFIG,
345
+ env_file=env_file,
346
+ env_file_overrides_shell=env_file_overrides_shell,
347
+ no_dotenv=no_dotenv,
348
+ storage_name=storage,
349
+ log_level=log_level,
350
+ setup_logging=setup_logging,
351
+ )
352
+ ```
353
+
354
+ ### Add the Generated Config CLI
355
+
356
+ ```python
357
+ config_app = MYAPP_CONFIG.typer_app(
358
+ state_type=CliState,
359
+ runtime_payload=lambda state: {
360
+ "storage": str(state.env_bootstrap.storage_root)
361
+ if state.env_bootstrap
362
+ else None,
363
+ },
364
+ )
365
+ app.add_typer(config_app, name="config")
366
+ ```
367
+
368
+ ### Edit Local Values in the Terminal
369
+
370
+ `config edit` opens a Textual editor. The editor shows:
371
+
372
+ - key number
373
+ - section
374
+ - env key
375
+ - shell status
376
+ - local value
377
+ - default value
378
+ - short explanation
379
+
380
+ Selecting a row opens a modal with type information, possible values, and the
381
+ long explanation. Secret values are redacted. Required missing values show
382
+ `<required>`.
383
+
384
+ ### Use Logging
385
+
386
+ ```python
387
+ from apprc.logging import get_logger, setup_logging
388
+
389
+ setup_logging(level="INFO", renderer="cli")
390
+ log = get_logger(__name__)
391
+
392
+ log.action_begin("Loading workspace")
393
+ log.success("Workspace ready", storage="default")
394
+ ```
395
+
396
+ <br>
397
+
398
+ # References
399
+
400
+ ### Important Modules
401
+
402
+ | Module | Look Here For |
403
+ |---|---|
404
+ | `apprc.config.schema` | `ConfigField`, `ConfigOwner`, field lookup, typed loading. |
405
+ | `apprc.config.kit` | `AppConfigKit`, the high-level app integration facade. |
406
+ | `apprc.config.environment` | CLI startup dotenv/bootstrap precedence. |
407
+ | `apprc.config.storage_registry` | `~/.config/<app>/*.toml` storage names. |
408
+ | `apprc.config.local_env` | `<storage>/.env.local` reads, writes, validation. |
409
+ | `apprc.config.tui` | Textual app and modal interactions. |
410
+ | `apprc.config.tui_rendering` | Pure table cell rendering and styles. |
411
+ | `apprc.cli.config_app` | Generated `config` Typer commands. |
412
+ | `apprc.cli.bootstrap` | Common root CLI bootstrap options. |
413
+ | `apprc.logging` | Logging facade: `setup_logging`, `get_logger`, `AppLogger`. |
414
+
415
+ ### Important Config Types
416
+
417
+ | Type | Meaning |
418
+ |---|---|
419
+ | `AppConfigKit` | Convenient object applications keep around. |
420
+ | `AppConfigSpec` | Frozen declaration behind the kit. |
421
+ | `ConfigOwner` | One config section, env prefix, runtime path, and fields. |
422
+ | `ConfigField` | One editable or read-only env-backed setting. |
423
+ | `BaseEnv` | Runtime dataclass base that binds values from env. |
424
+ | `EnvBootstrapResult` | Files and storage selected during CLI startup. |
425
+ | `StorageRegistry` | Parsed TOML registry. |
426
+ | `LocalEnvUpdate` | Result of writing one local dotenv override. |
427
+
428
+ ### Config CLI Commands
429
+
430
+ | Command | Purpose |
431
+ |---|---|
432
+ | `config init STORAGE_ROOT --name NAME --default` | Register a storage root. |
433
+ | `config doctor` | Diagnose registry and selected storage state. |
434
+ | `config show --json` | Print resolved runtime config payload. |
435
+ | `config set-default NAME` | Change default storage. |
436
+ | `config set KEY VALUE` | Write one local override. |
437
+ | `config edit` | Open the Textual editor. |
438
+
439
+ ### Logging Functions
440
+
441
+ | Function | Purpose |
442
+ |---|---|
443
+ | `setup_logging(...)` | Configure stdlib/structlog output. |
444
+ | `get_logger(__name__)` | Return an `AppLogger`. |
445
+ | `log.action_begin(...)` | Start a visible operation. |
446
+ | `log.success(...)` | Mark a completed operation. |
447
+ | `log.retry(...)` | Record retry attempts. |
448
+ | `log.fallback(...)` | Record fallback behavior. |
449
+ | `log.traceback(...)` | Emit exception information with redaction support. |
450
+ | `async_telemetry(...)` | Emit periodic async progress logs. |
451
+
452
+ <br>
453
+
454
+ # Development
455
+
456
+ ### Local Setup
457
+
458
+ ```shell
459
+ git clone https://github.com/HisQu/apprc.git
460
+ cd apprc
461
+
462
+ python -m venv .venv
463
+ source .venv/bin/activate
464
+ .venv/bin/python -m pip install --upgrade pip
465
+ .venv/bin/python -m pip install -e "." --group dev
466
+ ```
467
+
468
+ or:
469
+
470
+ ```shell
471
+ uv sync --frozen --all-groups
472
+ ```
473
+
474
+ ### Quality Gates
475
+
476
+ Run these before sending changes:
477
+
478
+ ```shell
479
+ ruff format .
480
+ ruff check .
481
+ pyright
482
+ pytest
483
+ ```
484
+
485
+ ### Test Against Haiu
486
+
487
+ Haiu is the main downstream integration test for AppRC. From the Haiu repo:
488
+
489
+ ```shell
490
+ cd ../haiu
491
+ .venv/bin/python -m pip install --no-deps --no-build-isolation -e ../apprc
492
+ .venv/bin/pytest tests/haiu/core/test_config_tui.py -q
493
+ .venv/bin/pytest
494
+ ```
495
+
496
+ Keep refactors behavior-preserving. If a cleanup would remove a public module,
497
+ change import-time side effects, or alter CLI output, treat that as a feature
498
+ change and ask first.