Command Line Interface¶
apywire provides a CLI for generating specs from class introspection and compiling specs to Python code.
Installation¶
The CLI is included with apywire:
Verify the installation:
Commands¶
generate¶
Generate a wiring spec by introspecting class constructors.
Arguments:
--format FORMAT- Output format:ini,toml, orjson(required)ENTRY- One or more class entries asmodule.Class name
Examples:
# Generate a spec for datetime.datetime
python -m apywire generate --format json "datetime.datetime now"
# Generate TOML and save to file
python -m apywire generate --format toml "datetime.datetime now" > config.toml
# Multiple entries
python -m apywire generate --format json \
"datetime.datetime now" \
"collections.OrderedDict config"
Output:
The generator introspects the class constructor and creates placeholders for each parameter:
compile¶
Compile a spec file to Python code.
Arguments:
--format FORMAT- Input format:ini,toml, orjson(required)--aio- Generate async accessors usingrun_in_executor--thread-safe- Generate thread-safe accessors with locking--emit-spec- Also emit the source spec as a module-level dictFILE- Input spec file path, or-to read from stdin
Examples:
# Compile a JSON spec
python -m apywire compile --format json config.json
# Compile with async support
python -m apywire compile --format toml --aio config.toml > wiring.py
# Compile with thread safety
python -m apywire compile --format ini --thread-safe config.ini
# Compile defaults that a user's config can later extend
python -m apywire compile --format toml --emit-spec defaults.toml > _defaults.py
# Read from stdin
cat config.json | python -m apywire compile --format json -
Output:
The compiler generates a Python module with a Compiled class:
import datetime
class Compiled:
def now(self):
if not hasattr(self, '_now'):
self._now = datetime.datetime(year=self.now_year(), ...)
return self._now
def now_year(self):
return 2025
compiled = Compiled()
merge¶
Overlay spec files and print the effective spec — what a config actually merges to. See Merging Specs for the semantics.
Arguments:
--format FORMAT- Input format:ini,toml, orjson(required)--output-format FORMAT- Output format (defaults to the input format)SOURCE- Specs in overlay order. Each is a file path,-for stdin, or an importablepkg.module:name
Examples:
# What does my config actually change?
python -m apywire merge --format toml defaults.toml user.toml
# Overlay a user's config onto compiled defaults (see --emit-spec)
python -m apywire merge --format toml myapp._defaults:spec user.toml
# Merge TOML, read the result as JSON
python -m apywire merge --format toml --output-format json base.toml user.toml
The pkg.module:name form is what pairs with --emit-spec: a compiled
container exposes its source spec as a module attribute, and that is the
base a user's config is overlaid onto.
TOML output requires tomli_w; without it, use --output-format json.
Full Workflow¶
A typical workflow combines generate and compile:
1. Generate a Spec¶
2. Customize the Spec¶
Edit config.toml with your values:
now_year = 2025
now_month = 6
now_day = 15
["datetime.datetime now"]
year = "{now_year}"
month = "{now_month}"
day = "{now_day}"
3. Compile to Python¶
4. Use in Your Application¶
Pipeline Usage¶
You can pipe generate output directly to compile:
# Generate and compile in one command
python -m apywire generate --format json "collections.OrderedDict cfg" \
| python -m apywire compile --format json -
Format Reference¶
JSON¶
Direct mapping—no special handling needed:
TOML¶
Top-level keys are constants, tables are wiring entries:
Quoted Section Names
TOML section names containing spaces must be quoted: ["module.Class name"]
INI¶
Uses [constants] section for constants (required by INI format):
Compiler Options¶
--aio¶
Adds async support via a .aio cached property (CompiledAio wrapper). All methods remain sync def; async access is provided through compiled.aio.name():
from functools import cached_property
from apywire.runtime import CompiledAio
class Compiled:
def now(self):
if not hasattr(self, '_now'):
self._now = datetime.datetime(...)
return self._now
@cached_property
def aio(self):
return CompiledAio(self)
--thread-safe¶
Generates thread-safe accessors with locking:
class Compiled(ThreadSafeMixin):
def __init__(self):
self._init_thread_safety()
def now(self):
if not hasattr(self, '_now'):
self._now = self._instantiate_attr('now', lambda: datetime.datetime(...))
return self._now
You can combine both flags:
--emit-spec¶
Also emits the source spec as a module-level dict, so the compiled module is self-describing:
import datetime
spec = {'y': 2025, 'datetime.date d': {'year': '{y}', 'month': 1, 'day': 1}}
class Compiled:
def d(self):
if not hasattr(self, '_d'):
self._d = datetime.date(year=self.y(), month=1, day=1)
return self._d
def y(self):
return 2025
compiled = Compiled()
Placeholders are emitted verbatim ('{y}' stays a string), so the spec
can be overlaid with merge_specs and wired again — which
is how compiled defaults stay extensible by a user's config.
A spec that cannot be emitted — one holding a value with no literal
source representation, or one wiring a module whose root package is named
spec (the emitted assignment would shadow its import) — prints
Error compiling spec: … and exits 1.
Next Steps¶
- Configuration Files - Loading specs from config files
- Merging Specs - Overlaying one spec on another
- Compilation - Understanding the compiled output
- Generator - Python API for the generator