Reusable runtime configuration, config CLI, and logging helpers for Python applications.
Project description
apprc is a small Python library for application runtime configuration and
logging. It is useful when a project needs typed env-backed config, packaged
defaults, per-user storage registries, local override files, a reusable
config CLI, a terminal config editor, and structured logs without rebuilding
that plumbing in every application.
The key idea: the application declares its config contract once, and apprc
derives the boring workflows from that contract.
Tech Stack:
typed-settingsfor typed config fields and runtime dataclasses.typerfor CLI commands.python-dotenvfor.envfile parsing and writing.textualfor the terminal config editor.structlogfor structured logging.
Installation
Important
Prerequisites
Install From PyPI
python -m pip install apprc
Use apprc As Project Dependency
Any Python build system supporting pyproject.toml (PEP 621) works.
For published releases, depend on the PyPI package:
[project]
dependencies = [
"apprc",
]
For the current repository revision, depend on the Git URL:
[project]
dependencies = [
"apprc @ git+https://github.com/HisQu/apprc.git",
]
Editable Install With pip
git clone https://github.com/HisQu/apprc.git
cd apprc
python -m venv .venv
source .venv/bin/activate
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e "." --group dev
Editable Install With uv
git clone https://github.com/HisQu/apprc.git
cd apprc
uv sync --frozen --all-groups
Verify
python -c "import apprc; print(apprc.AppConfigKit)"
pytest
Example
An application usually wires AppRC in three steps: declare fields, create the
kit, and mount the generated config CLI.
from dataclasses import dataclass
import typer
from apprc import AppConfigKit
from apprc.config import ConfigOwner, config_field
# 1) Declare the config fields your app owns.
CLIENT_OWNER = ConfigOwner(
key="client",
title="Client",
env_prefix="MYAPP_",
rc_path=("client",),
runtime_cls=None,
fields=(
config_field(
"model",
"MODEL",
str,
default="demo-model",
title="Model",
explanation="Model used by client calls.",
),
),
)
# 2) Create the reusable AppRC facade for your application.
MYAPP_CONFIG = AppConfigKit(
app_name="myapp",
display_name="MyApp",
config_package="myapp.config",
owners=(CLIENT_OWNER,),
storage_root_env_key="MYAPP_D_STORAGE",
registry_filename="myapp.toml",
shared_env_filename=".env.shared",
local_env_filename=".env.local",
)
# 3) Mount the generated `myapp config ...` command group.
@dataclass(slots=True)
class CliState:
env_bootstrap: object | None = None
storage: str | None = None
app = typer.Typer()
app.add_typer(
MYAPP_CONFIG.typer_app(state_type=CliState),
name="config",
)
Runtime dataclasses can inherit BaseEnv when you want typed objects populated
from the bootstrapped environment. The important first step is the owner
inventory: AppRC reuses it for loading, validation, docs, CLI commands, and the
terminal editor.
AppRC does not install its own apprc command. The config CLI is meant to be
mounted as a subcommand of the application that depends on AppRC.
Users then get:
myapp config setup
myapp config init /absolute/path/to/storage --name default --default
myapp config doctor
myapp config show --json
myapp config set client.model other-model
myapp config edit
Use myapp config setup for normal first-time installation. It explains the
config file and storage root locations, asks for a default storage, and prints
next steps. config init remains available as the lower-level command for
scripts or manual storage registration.
Explanations
Config Model
apprc separates config into clear layers:
| Layer | Owner | Purpose |
|---|---|---|
ConfigField |
application | One typed env-backed setting. |
ConfigOwner |
application | A named section of related fields. |
.env.shared |
application package | Packaged defaults shipped with code. |
<storage>/.env.local |
user/project | Per-storage local overrides. |
| shell environment | user/process | Highest-priority process values by default. |
~/.config/<app>/<registry_filename> |
AppRC registry | Named storage roots and default storage. |
Runtime dataclasses inherit BaseEnv. The dataclass owns Python attributes;
ConfigOwner owns env names, docs labels, editor labels, choices, and
redaction metadata.
Bootstrap Precedence
Applications call AppConfigKit.bootstrap(...) once at CLI startup. It merges:
- packaged
.env.shared - selected storage-local
.env.local - explicit
--env-file - current shell environment
By default, the shell wins over --env-file. Set
env_file_overrides_shell=True when an explicit file should win inside the
current process.
Storage Registries
Globally installed commands need to find user data without hardcoding one path.
apprc stores named roots in ~/.config/<app>/<registry_filename>, unless
the app-specific <APP>_CONFIG_FILE environment variable points to a custom
TOML file. For an app named myapp, that override is MYAPP_CONFIG_FILE.
The interactive setup command refuses custom locations unless that variable is
already set, because future commands must be able to rediscover the same file.
For example:
default_storage = "default"
[storages.default]
root = "/absolute/path/to/storage"
[archived_storages.old-default]
archive = "/absolute/path/to/old-default.apprc.tar.xz"
source_root = "/absolute/path/to/old-default"
Each storage root owns its own local override file, such as .env.local.
Archived storage records are only last-known restore shortcuts for the
terminal editor; runtime bootstrap still selects live directory entries from
[storages].
On POSIX/WSL hosts, Windows drive paths such as D:\Training\demo-project are
normalized to usable local paths before AppRC writes the registry or reads a
storage-root environment value.
Important
POSIX shells consume unquoted backslashes before Python sees the argument.
If you type C:\Projects\demo-storage without quotes, AppRC may receive
C:Projectsdemo-storage, which is not recoverable as a real Windows path.
Use one of these forms instead:
myapp config init 'C:\Projects\demo-storage' --name default --default
myapp config init C:/Projects/demo-storage --name default --default
myapp config init /mnt/c/Projects/demo-storage --name default --default
Logging
apprc.logging wraps stdlib logging and structlog. setup_logging() installs
formatters and dependency logger levels. get_logger() returns an AppLogger
with semantic helper methods such as action_begin, success, retry,
fallback, telemetry, and traceback.
Use AppRC logging when application logs should stay structured and readable in CLI output, notebooks, and tests.
Guides
Define Config Fields
Put config declarations in your application package, usually
myapp/config/owners.py.
from pathlib import Path
from apprc.config import CONFIG_MISSING, ConfigOwner, config_field
STORAGE_OWNER = ConfigOwner(
key="storage",
title="Storage",
env_prefix="MYAPP_",
rc_path=("storage",),
runtime_cls=None,
fields=(
config_field(
"root",
"D_STORAGE",
Path,
default=CONFIG_MISSING,
editable=False,
required=True,
explanation_short="Active storage root.",
explanation_long="Selected through the AppRC storage registry.",
),
),
)
Use editable=False for values owned by the registry instead of .env.local.
Bootstrap a CLI
Call bootstrap before creating runtime config objects.
from apprc.cli import bootstrap_cli_env
from apprc.logging import setup_logging
state.env_bootstrap = bootstrap_cli_env(
MYAPP_CONFIG,
env_file=env_file,
env_file_overrides_shell=env_file_overrides_shell,
no_dotenv=no_dotenv,
storage_name=storage,
log_level=log_level,
setup_logging=setup_logging,
)
Add the Generated Config CLI
config_app = MYAPP_CONFIG.typer_app(
state_type=CliState,
runtime_payload=lambda state: {
"storage": str(state.env_bootstrap.storage_root)
if state.env_bootstrap
else None,
},
)
app.add_typer(config_app, name="config")
Edit Local Values in the Terminal
config edit opens a Textual editor. The editor shows:
- key number
- section
- env key
- shell status
- local value
- default value
- short explanation
Selecting a row opens a modal with type information, possible values, and the
long explanation. Secret values are redacted. Required missing values show
<required>.
The editor also manages storage lifecycle:
New storageregisters a directory or restores a*.apprc.tar.xzarchive.Set this as default storagechanges the registry default.Delete storagecan unregister only or delete the directory too.Archive storagewrites*.apprc.tar.xzand can optionally remove the source directory after compression.
If the last live default is removed, the editor prompts for a replacement path
prefilled with ~/.local/share/<app>/default or offers to leave AppRC in the
fresh-install state with no default storage.
Use Logging
from apprc.logging import get_logger, setup_logging
setup_logging(level="INFO", renderer="cli")
log = get_logger(__name__)
log.action_begin("Loading workspace")
log.success("Workspace ready", storage="default")
References
Important Modules
| Module | Look Here For |
|---|---|
apprc.config.schema |
ConfigField, ConfigOwner, field lookup, typed loading. |
apprc.config.kit |
AppConfigKit, the high-level app integration facade. |
apprc.config.environment |
CLI startup dotenv/bootstrap precedence. |
apprc.config.paths |
Storage-root path normalization helpers. |
apprc.config.storage_registry |
~/.config/<app>/*.toml storage names. |
apprc.config.storage_archive |
*.apprc.tar.xz storage compression and restore. |
apprc.config.local_env |
<storage>/.env.local reads, writes, validation. |
apprc.config.tui |
Textual app and modal interactions. |
apprc.config.tui_rendering |
Pure table cell rendering and styles. |
apprc.cli.config_app |
Generated config Typer commands. |
apprc.cli.bootstrap |
Common root CLI bootstrap options. |
apprc.logging |
Logging facade: setup_logging, get_logger, AppLogger. |
Important Config Types
| Type | Meaning |
|---|---|
AppConfigKit |
Convenient object applications keep around. |
AppConfigSpec |
Frozen declaration behind the kit. |
ConfigOwner |
One config section, env prefix, runtime path, and fields. |
ConfigField |
One editable or read-only env-backed setting. |
BaseEnv |
Runtime dataclass base that binds values from env. |
EnvBootstrapResult |
Files and storage selected during CLI startup. |
StorageRegistry |
Parsed TOML registry. |
ArchivedStorageRecord |
Last-known archive path for editor restore shortcuts. |
LocalEnvUpdate |
Result of writing one local dotenv override. |
Config CLI Commands
| Command | Purpose |
|---|---|
config setup |
Interactively choose the registry file and default storage. |
config init STORAGE_ROOT --name NAME --default |
Register a storage root. |
config doctor |
Diagnose registry and selected storage state. |
config show --json |
Print resolved runtime config payload. |
config set-default NAME |
Change default storage. |
config set KEY VALUE |
Write one local override. |
config edit |
Open the Textual editor. |
Logging Functions
| Function | Purpose |
|---|---|
setup_logging(...) |
Configure stdlib/structlog output. |
get_logger(__name__) |
Return an AppLogger. |
log.action_begin(...) |
Start a visible operation. |
log.success(...) |
Mark a completed operation. |
log.retry(...) |
Record retry attempts. |
log.fallback(...) |
Record fallback behavior. |
log.traceback(...) |
Emit exception information with redaction support. |
async_telemetry(...) |
Emit periodic async progress logs. |
Development
Local Setup
git clone https://github.com/HisQu/apprc.git
cd apprc
python -m venv .venv
source .venv/bin/activate
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e "." --group dev
or:
uv sync --frozen --all-groups
Quality Gates
Run these before sending changes:
ruff format .
ruff check .
pyright
pytest
Test Against Haiu
Haiu is the main downstream integration test for AppRC. From the Haiu repo:
cd ../haiu
.venv/bin/python -m pip install --no-deps --no-build-isolation -e ../apprc
.venv/bin/pytest tests/haiu/core/test_config_tui.py -q
.venv/bin/pytest
Keep refactors behavior-preserving. If a cleanup would remove a public module, change import-time side effects, or alter CLI output, treat that as a feature change and ask first.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file apprc-0.2.1.tar.gz.
File metadata
- Download URL: apprc-0.2.1.tar.gz
- Upload date:
- Size: 92.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
983232b24bde5eacbb00ded73845b188c55f2cca29f1e8a3582209ead700588c
|
|
| MD5 |
64d9c54550c00d1c0477aabd73f588f4
|
|
| BLAKE2b-256 |
1b8cb5ec1c809e4bb6ac57ea55d3a1898027d8b7ddf74c7193e629a3a0d196c3
|
File details
Details for the file apprc-0.2.1-py3-none-any.whl.
File metadata
- Download URL: apprc-0.2.1-py3-none-any.whl
- Upload date:
- Size: 99.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84c0015ecb3288538abe90126f4a914bb2e85887959161ad70b3ceee51650b9a
|
|
| MD5 |
444e811e8dfb3d288af16ce6be28d112
|
|
| BLAKE2b-256 |
7caa6aca5580581f7b8879af1a2fd6d26b4af95c59f592825c5c4fdded37bd2f
|