apprc-core 0.25.0__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.
Files changed (153) hide show
  1. apprc/__init__.py +21 -0
  2. apprc/__main__.py +7 -0
  3. apprc/_cli_app.py +116 -0
  4. apprc/_dotenv_guard.py +29 -0
  5. apprc/_exports.py +18 -0
  6. apprc/_lazy.py +62 -0
  7. apprc/cli/__init__.py +7 -0
  8. apprc/cli/__init__.pyi +65 -0
  9. apprc/cli/_facade.py +81 -0
  10. apprc/definition/__init__.py +1 -0
  11. apprc/definition/app_config/__init__.py +7 -0
  12. apprc/definition/app_config/_validation.py +57 -0
  13. apprc/definition/app_config/spec.py +214 -0
  14. apprc/definition/app_config/storage.py +20 -0
  15. apprc/definition/app_config/user_dotenv.py +19 -0
  16. apprc/definition/env_config/__init__.py +19 -0
  17. apprc/definition/env_config/_validation.py +154 -0
  18. apprc/definition/env_config/conversion.py +93 -0
  19. apprc/definition/env_config/lookup.py +79 -0
  20. apprc/definition/env_config/schema.py +185 -0
  21. apprc/definition/env_config/sentinels.py +17 -0
  22. apprc/definition/provenance.py +108 -0
  23. apprc/definition/resolution.py +86 -0
  24. apprc/files/__init__.py +25 -0
  25. apprc/files/_exports.py +19 -0
  26. apprc/interfaces/__init__.py +7 -0
  27. apprc/interfaces/__init__.pyi +33 -0
  28. apprc/interfaces/_facade.py +45 -0
  29. apprc/interfaces/_setup_text.py +165 -0
  30. apprc/interfaces/_terminal_styles.py +129 -0
  31. apprc/interfaces/cli/__init__.py +64 -0
  32. apprc/interfaces/cli/_errors.py +20 -0
  33. apprc/interfaces/cli/_interactive_setup.py +207 -0
  34. apprc/interfaces/cli/_resolution.py +113 -0
  35. apprc/interfaces/cli/_typer_utils.py +261 -0
  36. apprc/interfaces/cli/config_command/__init__.py +27 -0
  37. apprc/interfaces/cli/config_command/_base.py +414 -0
  38. apprc/interfaces/cli/config_command/_editor_commands.py +106 -0
  39. apprc/interfaces/cli/config_command/_editor_launcher.py +190 -0
  40. apprc/interfaces/cli/config_command/_handlers.py +55 -0
  41. apprc/interfaces/cli/config_command/_output.py +146 -0
  42. apprc/interfaces/cli/config_command/_prompts.py +185 -0
  43. apprc/interfaces/cli/config_command/_runtime_commands.py +486 -0
  44. apprc/interfaces/cli/config_command/_runtime_payload.py +58 -0
  45. apprc/interfaces/cli/config_command/_selector_context.py +219 -0
  46. apprc/interfaces/cli/config_command/_storage_commands.py +201 -0
  47. apprc/interfaces/cli/config_command/app.py +479 -0
  48. apprc/interfaces/cli/config_command/group_options.py +50 -0
  49. apprc/interfaces/cli/config_command/state.py +328 -0
  50. apprc/interfaces/cli/context.py +288 -0
  51. apprc/interfaces/cli/diagnostics/__init__.py +13 -0
  52. apprc/interfaces/cli/diagnostics/_diagnosis.py +191 -0
  53. apprc/interfaces/cli/diagnostics/messages.py +191 -0
  54. apprc/interfaces/cli/diagnostics/payload.py +220 -0
  55. apprc/interfaces/cli/diagnostics/status.py +17 -0
  56. apprc/interfaces/cli/doctor_output.py +334 -0
  57. apprc/interfaces/cli/mount.py +251 -0
  58. apprc/interfaces/cli/options.py +73 -0
  59. apprc/interfaces/cli/runtime.py +802 -0
  60. apprc/interfaces/cli/setup_command.py +331 -0
  61. apprc/interfaces/tui/__init__.py +9 -0
  62. apprc/interfaces/tui/_field_state.py +139 -0
  63. apprc/interfaces/tui/_primitives.py +359 -0
  64. apprc/interfaces/tui/_rendering.py +413 -0
  65. apprc/interfaces/tui/_styles.py +78 -0
  66. apprc/interfaces/tui/_value_modal_rendering.py +202 -0
  67. apprc/interfaces/tui/editor/__init__.py +8 -0
  68. apprc/interfaces/tui/editor/app.py +1101 -0
  69. apprc/interfaces/tui/editor/setup.py +339 -0
  70. apprc/interfaces/tui/editor/storage_archive.py +102 -0
  71. apprc/interfaces/tui/editor/storage_base.py +19 -0
  72. apprc/interfaces/tui/editor/storage_editing.py +256 -0
  73. apprc/interfaces/tui/editor/storage_registration.py +180 -0
  74. apprc/interfaces/tui/editor/storage_removal.py +111 -0
  75. apprc/interfaces/tui/editor/workflows.py +240 -0
  76. apprc/interfaces/tui/modals/__init__.py +11 -0
  77. apprc/interfaces/tui/modals/screens.py +604 -0
  78. apprc/interfaces/tui/setup/__init__.py +5 -0
  79. apprc/interfaces/tui/setup/app.py +32 -0
  80. apprc/interfaces/tui/storage/__init__.py +19 -0
  81. apprc/interfaces/tui/storage/entries.py +138 -0
  82. apprc/interfaces/tui/storage/selection.py +62 -0
  83. apprc/provenance/__init__.py +23 -0
  84. apprc/provenance/_exports.py +19 -0
  85. apprc/public/__init__.py +8 -0
  86. apprc/public/app_rc.py +749 -0
  87. apprc/public/config.py +5 -0
  88. apprc/public/field.py +196 -0
  89. apprc/py.typed +1 -0
  90. apprc/runtime/__init__.py +1 -0
  91. apprc/runtime/_selection.py +15 -0
  92. apprc/runtime/config/__init__.py +1 -0
  93. apprc/runtime/config/_binding.py +157 -0
  94. apprc/runtime/config/_defaults.py +79 -0
  95. apprc/runtime/config/_loading.py +79 -0
  96. apprc/runtime/config/_origins.py +75 -0
  97. apprc/runtime/config/_post_env_overrides.py +197 -0
  98. apprc/runtime/config/_runtime_validation.py +105 -0
  99. apprc/runtime/config/_state_transfer.py +166 -0
  100. apprc/runtime/config/base.py +373 -0
  101. apprc/runtime/config/config.py +411 -0
  102. apprc/runtime/logging.py +10 -0
  103. apprc/runtime/provenance/__init__.py +25 -0
  104. apprc/runtime/provenance/formatting.py +15 -0
  105. apprc/runtime/provenance/python.py +148 -0
  106. apprc/runtime/resolution.py +388 -0
  107. apprc/scaffold/__init__.py +9 -0
  108. apprc/scaffold/config_package.py +306 -0
  109. apprc/schema/__init__.py +21 -0
  110. apprc/schema/_exports.py +13 -0
  111. apprc/services/__init__.py +1 -0
  112. apprc/services/inspection.py +98 -0
  113. apprc/services/manager.py +585 -0
  114. apprc/storage/__init__.py +30 -0
  115. apprc/storage/_exports.py +18 -0
  116. apprc/tui/__init__.py +5 -0
  117. apprc/tui/__init__.pyi +6 -0
  118. apprc/tui/_facade.py +13 -0
  119. apprc/user_files/__init__.py +1 -0
  120. apprc/user_files/app_home/__init__.py +17 -0
  121. apprc/user_files/app_home/_package_resources.py +36 -0
  122. apprc/user_files/app_home/_paths.py +24 -0
  123. apprc/user_files/app_home/application.py +106 -0
  124. apprc/user_files/app_home/locations.py +228 -0
  125. apprc/user_files/app_home/writes.py +42 -0
  126. apprc/user_files/env_files/__init__.py +19 -0
  127. apprc/user_files/env_files/_document.py +250 -0
  128. apprc/user_files/env_files/_parsing.py +61 -0
  129. apprc/user_files/env_files/files.py +126 -0
  130. apprc/user_files/env_files/layers.py +81 -0
  131. apprc/user_files/env_files/updates.py +358 -0
  132. apprc/user_files/env_files/values.py +44 -0
  133. apprc/user_files/migration.py +802 -0
  134. apprc/user_files/purge.py +261 -0
  135. apprc/user_files/setup/__init__.py +9 -0
  136. apprc/user_files/setup/flow.py +248 -0
  137. apprc/user_files/storage_roots/__init__.py +1 -0
  138. apprc/user_files/storage_roots/_io.py +275 -0
  139. apprc/user_files/storage_roots/_loading.py +257 -0
  140. apprc/user_files/storage_roots/_naming.py +41 -0
  141. apprc/user_files/storage_roots/archive.py +344 -0
  142. apprc/user_files/storage_roots/model.py +71 -0
  143. apprc/user_files/storage_roots/move.py +316 -0
  144. apprc/user_files/storage_roots/paths.py +142 -0
  145. apprc/user_files/storage_roots/readiness.py +83 -0
  146. apprc/user_files/storage_roots/registry.py +498 -0
  147. apprc/user_files/storage_roots/selector.py +415 -0
  148. apprc/utils/__init__.py +12 -0
  149. apprc/utils/stdlib.py +143 -0
  150. apprc_core-0.25.0.dist-info/METADATA +172 -0
  151. apprc_core-0.25.0.dist-info/RECORD +153 -0
  152. apprc_core-0.25.0.dist-info/WHEEL +4 -0
  153. apprc_core-0.25.0.dist-info/licenses/LICENSE +21 -0
apprc/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """Clean public facade for AppRC."""
2
+
3
+ # ruff: noqa: F401
4
+
5
+ # > Explicit aliases mark public imports for downstream type checkers.
6
+ from apprc.public.app_rc import AppRC as AppRC
7
+ from apprc.definition.resolution import ResolveOptions as ResolveOptions
8
+ from apprc.runtime.resolution import ResolvedConfig as ResolvedConfig
9
+ from apprc.public.config import Config as Config, ConfigBase as ConfigBase
10
+ from apprc.public.field import field as field
11
+ from apprc.definition.app_config.storage import Storage as Storage
12
+ from apprc.definition.app_config.user_dotenv import UserDotenv as UserDotenv
13
+
14
+ from . import cli
15
+ from . import tui
16
+ from . import files
17
+ from . import provenance
18
+ from . import schema
19
+ from . import storage
20
+
21
+ from ._exports import PUBLIC_NAMES as __all__
apprc/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Run AppRC's command-line application with ``python -m apprc``."""
2
+
3
+ from apprc._cli_app import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ main()
apprc/_cli_app.py ADDED
@@ -0,0 +1,116 @@
1
+ """Command-line entrypoint for AppRC development helpers."""
2
+
3
+ # == Standard Library ========================
4
+ from pathlib import Path
5
+ from typing import Annotated
6
+
7
+ # == 3rd Party ===============================
8
+ import typer
9
+
10
+ # == Internal ================================
11
+ from apprc.scaffold import (
12
+ ConfigScaffoldRequest,
13
+ scaffold_config_package,
14
+ )
15
+
16
+ app = typer.Typer(
17
+ help="Developer tools for AppRC-enabled applications.",
18
+ no_args_is_help=True,
19
+ pretty_exceptions_show_locals=False,
20
+ )
21
+ scaffold_app = typer.Typer(
22
+ help="Generate AppRC integration files.",
23
+ no_args_is_help=True,
24
+ )
25
+ app.add_typer(scaffold_app, name="scaffold")
26
+
27
+
28
+ @scaffold_app.command("config")
29
+ def scaffold_config_cmd(
30
+ package: Annotated[
31
+ str,
32
+ typer.Option(
33
+ "--package",
34
+ help="Import package that receives config/, for example myapp.",
35
+ ),
36
+ ],
37
+ app_id: Annotated[
38
+ str,
39
+ typer.Option("--app-id", help="Stable AppRC application identity."),
40
+ ],
41
+ user_dotenv: Annotated[
42
+ bool,
43
+ typer.Option(
44
+ "--user-dotenv",
45
+ help="Generate a declaration with a managed user dotenv.",
46
+ ),
47
+ ] = False,
48
+ storage: Annotated[
49
+ bool,
50
+ typer.Option(
51
+ "--storage",
52
+ help="Generate an app declaration with AppRC storage.",
53
+ ),
54
+ ] = False,
55
+ display_name: Annotated[
56
+ str | None,
57
+ typer.Option(
58
+ "--display-name",
59
+ help="Human-readable application name.",
60
+ ),
61
+ ] = None,
62
+ storage_selector_env_key: Annotated[
63
+ str | None,
64
+ typer.Option(
65
+ "--storage-selector-env-key",
66
+ help="Optional storage selector env key override.",
67
+ ),
68
+ ] = None,
69
+ env_prefix: Annotated[
70
+ str | None,
71
+ typer.Option(
72
+ "--env-prefix",
73
+ help="Full env prefix for the generated AppSection.",
74
+ ),
75
+ ] = None,
76
+ target: Annotated[
77
+ Path,
78
+ typer.Option(
79
+ "--target",
80
+ help="Source root that contains the package.",
81
+ ),
82
+ ] = Path("src"),
83
+ force: Annotated[
84
+ bool,
85
+ typer.Option(
86
+ "--force",
87
+ help="Replace existing generated config files.",
88
+ ),
89
+ ] = False,
90
+ ) -> None:
91
+ """Generate AppRC's standard ``X.config`` package layout."""
92
+ try:
93
+ result = scaffold_config_package(
94
+ ConfigScaffoldRequest(
95
+ package=package,
96
+ app_id=app_id,
97
+ display_name=display_name,
98
+ target=target,
99
+ user_dotenv=user_dotenv,
100
+ storage=storage,
101
+ storage_selector_env_key=storage_selector_env_key,
102
+ env_prefix=env_prefix,
103
+ force=force,
104
+ )
105
+ )
106
+ except (FileExistsError, ValueError) as exc:
107
+ raise typer.BadParameter(str(exc)) from exc
108
+
109
+ typer.echo(f"Created AppRC config package: {result.config_package_dir}")
110
+ for path in result.written_files:
111
+ typer.echo(f"- {path}")
112
+
113
+
114
+ def main() -> None:
115
+ """Run the AppRC command-line application."""
116
+ app()
apprc/_dotenv_guard.py ADDED
@@ -0,0 +1,29 @@
1
+ """Stdlib-only helpers for guarding dotenv autoload side effects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Iterator
7
+ from contextlib import contextmanager
8
+
9
+
10
+ @contextmanager
11
+ def _disable_dotenv_autoload() -> Iterator[None]:
12
+ """Temporarily disable libraries that auto-load ``.env`` files.
13
+
14
+ Some libraries import ``python-dotenv`` through their dependency graph.
15
+ Setting ``PYTHON_DOTENV_DISABLED`` around those imports preserves the
16
+ application's explicit environment loading policy while restoring the
17
+ user's original process environment afterward.
18
+
19
+ :return: Context manager that restores ``PYTHON_DOTENV_DISABLED``.
20
+ """
21
+ prev = os.environ.get("PYTHON_DOTENV_DISABLED")
22
+ os.environ["PYTHON_DOTENV_DISABLED"] = "1"
23
+ try:
24
+ yield
25
+ finally:
26
+ if prev is None:
27
+ os.environ.pop("PYTHON_DOTENV_DISABLED", None)
28
+ else:
29
+ os.environ["PYTHON_DOTENV_DISABLED"] = prev
apprc/_exports.py ADDED
@@ -0,0 +1,18 @@
1
+ """Supported names exported by the public facade."""
2
+
3
+ PUBLIC_NAMES = [
4
+ "AppRC",
5
+ "ResolveOptions",
6
+ "ResolvedConfig",
7
+ "Storage",
8
+ "UserDotenv",
9
+ "Config",
10
+ "ConfigBase",
11
+ "field",
12
+ "cli",
13
+ "tui",
14
+ "files",
15
+ "provenance",
16
+ "schema",
17
+ "storage",
18
+ ]
apprc/_lazy.py ADDED
@@ -0,0 +1,62 @@
1
+ """Small helpers for PEP 562 package facades. This speeds up import time a lot
2
+
3
+ Once Python 3.15 hits, replace all this logic with `lazy import`
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ from collections.abc import Callable, Mapping, Sequence
10
+ from importlib import import_module
11
+
12
+
13
+ type LazyGetattr = Callable[[str], object]
14
+ type LazyDir = Callable[[], list[str]]
15
+
16
+
17
+ def build_lazy_facade(
18
+ *,
19
+ public_module: str,
20
+ all_exports: Sequence[str],
21
+ module_exports: Mapping[str, str],
22
+ symbol_exports: Mapping[str, str],
23
+ ) -> tuple[list[str], LazyGetattr, LazyDir]:
24
+ """Build module-level hooks for one lazy public facade.
25
+
26
+ The returned ``__getattr__`` resolves modules and symbols only when a caller
27
+ first touches them. Resolved values are stored on the public module, not on
28
+ the private ``_facade`` module, so repeated access is the same cheap global
29
+ lookup that an eager facade would provide.
30
+
31
+ :param public_module: Public module name that owns the lazy attributes.
32
+ :param all_exports: Stable ``__all__`` values for the facade.
33
+ :param module_exports: Exported submodule names to import paths.
34
+ :param symbol_exports: Exported symbol names to the module that defines them.
35
+ :return: ``(__all__, __getattr__, __dir__)`` for re-export by ``__init__``.
36
+ """
37
+ all_names = list(all_exports)
38
+ module_map = dict(module_exports)
39
+ symbol_map = dict(symbol_exports)
40
+ known_names = frozenset((*module_map, *symbol_map))
41
+
42
+ def __getattr__(name: str) -> object:
43
+ namespace = sys.modules[public_module].__dict__
44
+ if name in module_map:
45
+ value = import_module(module_map[name])
46
+ else:
47
+ try:
48
+ module_name = symbol_map[name]
49
+ except KeyError as exc:
50
+ raise AttributeError(
51
+ f"module {public_module!r} has no attribute {name!r}"
52
+ ) from exc
53
+ module = import_module(module_name)
54
+ value = getattr(module, name)
55
+ namespace[name] = value
56
+ return value
57
+
58
+ def __dir__() -> list[str]:
59
+ namespace = sys.modules[public_module].__dict__
60
+ return sorted(set(namespace) | known_names | set(all_names))
61
+
62
+ return all_names, __getattr__, __dir__
apprc/cli/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Advanced AppRC CLI integration namespace."""
2
+
3
+ # ruff: noqa: F401
4
+
5
+ from apprc.cli._facade import __all__ as __all__
6
+ from apprc.cli._facade import __dir__ as __dir__
7
+ from apprc.cli._facade import __getattr__ as __getattr__
apprc/cli/__init__.pyi ADDED
@@ -0,0 +1,65 @@
1
+ """Typed surface for the lazy AppRC CLI integration namespace."""
2
+
3
+ # ruff: noqa: F401
4
+
5
+ from apprc.interfaces.cli import (
6
+ COMMON_CLI_FLAG_OPTIONS as COMMON_CLI_FLAG_OPTIONS,
7
+ COMMON_CLI_VALUE_OPTIONS as COMMON_CLI_VALUE_OPTIONS,
8
+ DEFAULT_CONFIG_RUNTIME_INDEPENDENT_ACTIONS as DEFAULT_CONFIG_RUNTIME_INDEPENDENT_ACTIONS,
9
+ MISSING_ACTION_MESSAGE as MISSING_ACTION_MESSAGE,
10
+ RuntimeIndependentCommand as RuntimeIndependentCommand,
11
+ CliArgvProvider as CliArgvProvider,
12
+ CliRuntime as CliRuntime,
13
+ CliRuntimeContext as CliRuntimeContext,
14
+ CliRuntimeOptions as CliRuntimeOptions,
15
+ CliRuntimeOptionsProtocol as CliRuntimeOptionsProtocol,
16
+ CliRuntimePolicy as CliRuntimePolicy,
17
+ CliRuntimeSession as CliRuntimeSession,
18
+ CliRuntimeStateFactory as CliRuntimeStateFactory,
19
+ ConfigCliState as ConfigCliState,
20
+ ConfigRuntimePolicy as ConfigRuntimePolicy,
21
+ ConfigSelectorContext as ConfigSelectorContext,
22
+ DefaultConfigCliState as DefaultConfigCliState,
23
+ EnvFileOverridesOption as EnvFileOverridesOption,
24
+ EnvFilesOption as EnvFilesOption,
25
+ LogLevelOption as LogLevelOption,
26
+ MountCliRuntimeStateFactory as MountCliRuntimeStateFactory,
27
+ SkipDotenvLayersOption as SkipDotenvLayersOption,
28
+ StorageOption as StorageOption,
29
+ active_storage_root_from_state as active_storage_root_from_state,
30
+ args_after_command as args_after_command,
31
+ resolve_cli_config as resolve_cli_config,
32
+ resolve_cli_options as resolve_cli_options,
33
+ build_config_doctor_payload as build_config_doctor_payload,
34
+ build_config_typer_app as build_config_typer_app,
35
+ cli_options_from as cli_options_from,
36
+ cli_runtime_context_from as cli_runtime_context_from,
37
+ cli_runtime_options_to_args as cli_runtime_options_to_args,
38
+ config_command_text as config_command_text,
39
+ config_request_skips_runtime as config_request_skips_runtime,
40
+ config_setup_message as config_setup_message,
41
+ dump_json as dump_json,
42
+ ensure_config_group_name_available as ensure_config_group_name_available,
43
+ exit_missing_action as exit_missing_action,
44
+ initial_storage_from_state as initial_storage_from_state,
45
+ mount_config_cli as mount_config_cli,
46
+ parse_log_level as parse_log_level,
47
+ prepare_cli_runtime_context as prepare_cli_runtime_context,
48
+ print_config_doctor as print_config_doctor,
49
+ print_config_paths as print_config_paths,
50
+ run_typer_app as run_typer_app,
51
+ state_from as state_from,
52
+ strip_leading_options as strip_leading_options,
53
+ )
54
+ from apprc.interfaces.cli.diagnostics import (
55
+ ConfigDoctorPayload as ConfigDoctorPayload,
56
+ ConfigDoctorStatus as ConfigDoctorStatus,
57
+ )
58
+ from apprc.runtime.logging import (
59
+ ResolutionLogger as ResolutionLogger,
60
+ )
61
+
62
+ __all__: list[str]
63
+
64
+ def __getattr__(name: str) -> object: ...
65
+ def __dir__() -> list[str]: ...
apprc/cli/_facade.py ADDED
@@ -0,0 +1,81 @@
1
+ """Lazy facade for advanced AppRC CLI integration APIs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from apprc._lazy import build_lazy_facade
6
+
7
+ _CLI_EXPORTS = [
8
+ "COMMON_CLI_FLAG_OPTIONS",
9
+ "COMMON_CLI_VALUE_OPTIONS",
10
+ "DEFAULT_CONFIG_RUNTIME_INDEPENDENT_ACTIONS",
11
+ "MISSING_ACTION_MESSAGE",
12
+ "RuntimeIndependentCommand",
13
+ "CliArgvProvider",
14
+ "CliRuntime",
15
+ "CliRuntimeContext",
16
+ "CliRuntimeOptions",
17
+ "CliRuntimeOptionsProtocol",
18
+ "CliRuntimePolicy",
19
+ "CliRuntimeSession",
20
+ "CliRuntimeStateFactory",
21
+ "ConfigCliState",
22
+ "ConfigRuntimePolicy",
23
+ "ConfigSelectorContext",
24
+ "DefaultConfigCliState",
25
+ "EnvFileOverridesOption",
26
+ "EnvFilesOption",
27
+ "LogLevelOption",
28
+ "MountCliRuntimeStateFactory",
29
+ "SkipDotenvLayersOption",
30
+ "StorageOption",
31
+ "active_storage_root_from_state",
32
+ "args_after_command",
33
+ "resolve_cli_config",
34
+ "resolve_cli_options",
35
+ "build_config_doctor_payload",
36
+ "build_config_typer_app",
37
+ "cli_options_from",
38
+ "cli_runtime_context_from",
39
+ "cli_runtime_options_to_args",
40
+ "config_command_text",
41
+ "config_request_skips_runtime",
42
+ "config_setup_message",
43
+ "dump_json",
44
+ "ensure_config_group_name_available",
45
+ "exit_missing_action",
46
+ "initial_storage_from_state",
47
+ "mount_config_cli",
48
+ "parse_log_level",
49
+ "prepare_cli_runtime_context",
50
+ "print_config_doctor",
51
+ "print_config_paths",
52
+ "run_typer_app",
53
+ "state_from",
54
+ "strip_leading_options",
55
+ ]
56
+ _DIAGNOSTIC_EXPORTS = [
57
+ "ConfigDoctorPayload",
58
+ "ConfigDoctorStatus",
59
+ ]
60
+ _RESULT_EXPORTS = [
61
+ "ResolutionLogger",
62
+ ]
63
+
64
+ _SYMBOL_EXPORTS = {
65
+ **{name: "apprc.interfaces.cli" for name in _CLI_EXPORTS},
66
+ **{
67
+ name: "apprc.interfaces.cli.diagnostics" for name in _DIAGNOSTIC_EXPORTS
68
+ },
69
+ **{name: "apprc.runtime.logging" for name in _RESULT_EXPORTS},
70
+ }
71
+
72
+ __all__, __getattr__, __dir__ = build_lazy_facade(
73
+ public_module="apprc.cli",
74
+ all_exports=[
75
+ *_CLI_EXPORTS,
76
+ *_DIAGNOSTIC_EXPORTS,
77
+ *_RESULT_EXPORTS,
78
+ ],
79
+ module_exports={},
80
+ symbol_exports=_SYMBOL_EXPORTS,
81
+ )
@@ -0,0 +1 @@
1
+ """Internal configuration contract packages."""
@@ -0,0 +1,7 @@
1
+ """Application-level configuration declarations and internal facades."""
2
+
3
+ # ruff: noqa: F401
4
+
5
+ from apprc.definition.app_config.spec import AppConfigSpec
6
+ from apprc.definition.app_config.storage import Storage
7
+ from apprc.definition.app_config.user_dotenv import UserDotenv
@@ -0,0 +1,57 @@
1
+ """Validation and derivation helpers for application config specs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ # == Standard Library ========================
6
+ import re
7
+
8
+
9
+ def _environment_key_prefix(app_id: str) -> str:
10
+ """Return the normalized environment-key prefix for an application.
11
+
12
+ :param app_id: Stable application identity.
13
+ :return: Uppercase identifier suitable for an environment key.
14
+ """
15
+ normalized = re.sub(r"[^A-Za-z0-9]+", "_", app_id).strip("_").upper()
16
+ return normalized or "APP"
17
+
18
+
19
+ def derive_apprc_dir_env_key(app_id: str) -> str:
20
+ """Return the environment key that relocates the AppRC directory.
21
+
22
+ :param app_id: Stable application identity.
23
+ :return: Uppercase ``<APP>_APPRC_DIR`` key.
24
+ """
25
+ return f"{_environment_key_prefix(app_id)}_APPRC_DIR"
26
+
27
+
28
+ def derive_legacy_apprc_toml_env_key(app_id: str) -> str:
29
+ """Return the released 0.19 AppRC TOML relocation key.
30
+
31
+ :param app_id: Legacy application identity.
32
+ :return: Uppercase ``<APP>_APPRC_TOML`` key.
33
+ """
34
+ return f"{_environment_key_prefix(app_id)}_APPRC_TOML"
35
+
36
+
37
+ def derive_storage_selector_env_key(app_id: str) -> str:
38
+ """Return the environment variable that selects active storage.
39
+
40
+ :param app_id: Application name from the AppRC integration spec.
41
+ :return: Uppercase env key for the active storage selector.
42
+ """
43
+ return f"{_environment_key_prefix(app_id)}_STORAGE"
44
+
45
+
46
+ def resolve_storage_selector_env_key(
47
+ *,
48
+ app_id: str,
49
+ selector_env_key: str | None,
50
+ ) -> str:
51
+ """Return the explicit or derived active-storage selector key.
52
+
53
+ :param app_id: Application name used for the conventional key.
54
+ :param selector_env_key: Optional caller-supplied key.
55
+ :return: Explicit key or the derived ``<APP>_STORAGE`` key.
56
+ """
57
+ return selector_env_key or derive_storage_selector_env_key(app_id)