epythet.userconfig¶
User-level defaults and parametrizable text snippets.
epythet ships opinions (what a README section for agents should say, which humour lines introduce the “for humans” pointer, whether a missing section is a warning or something to add). A user who wants different opinions sets them once, outside any repository, and every project on that machine picks them up. Two things live under the user’s config directory:
config.toml: policy. The[readme]table decides what theepythet-agentic-readmeskill does (agentic_aspects = "warn" | "add",humor,agentic_first); the[snippets]table can pointdirat a different snippet folder.snippets/<name>.md: text overrides. A snippet is looked up in the user’s folder first, then in the packaged defaults (epythet/data/snippets).epythet snippets initcopies the packaged defaults out once, with a header recording the epythet version they came from, and never overwrites a file that exists;epythet snippets diffshows how a user’s copy differs from the current packaged default, so upstream changes are visible without ever being applied silently.
The config directory is $EPYTHET_CONFIG_DIR, else $XDG_CONFIG_HOME/epythet,
else ~/.config/epythet: the config-side twin of
epythet.validation.ledger.user_data_dir(), which holds mutable data
(ledger observations) under ~/.local/share/epythet. Skills stay prose: they
call epythet snippets show <name> and epythet ai-readme-check --format json
and let this module do the resolving.
>>> import os, tempfile
>>> _saved = os.environ.get("EPYTHET_CONFIG_DIR")
>>> os.environ["EPYTHET_CONFIG_DIR"] = tempfile.mkdtemp()
>>> load_user_config().readme
ReadmePolicy(agentic_aspects='warn', humor=False, agentic_first=False)
>>> snippet("agentic-readme-humor").source
'packaged'
>>> written = init_snippets()
>>> snippet("agentic-readme-humor").source
'user'
>>> init_snippets() # a second init writes nothing
[]
>>> _ = os.environ.pop("EPYTHET_CONFIG_DIR") if _saved is None else os.environ.__setitem__("EPYTHET_CONFIG_DIR", _saved)
Module Attributes
Environment variable overriding the whole config directory. |
|
The policy file inside the config directory. |
|
The snippet folder inside the config directory (unless |
|
Where the packaged default snippets live. |
|
What |
|
The |
Functions
|
|
The policy file: |
|
|
A unified diff from the current packaged default to the user's copy ( |
epythet's version: the checkout's |
|
|
The epythet version recorded in a user copy's header ( |
|
Copy the packaged defaults into the user snippet folder; returns the paths written. |
|
Every available snippet, resolved, in name order. |
|
Read |
The names of the snippets epythet ships, sorted. |
|
|
The non-empty, non-comment lines of a pool snippet (one candidate per line). |
|
Resolve |
|
The provenance line |
|
Every snippet name available: packaged plus user-only files, sorted. |
|
The effective body of |
|
Show how the user's copy of a snippet differs from the current packaged default. |
|
Where user snippets are read: |
|
Copy the packaged default snippets into the user snippet folder, once. |
List every snippet with its source (user or packaged), provenance and status. |
|
|
Print the effective text of a snippet: the user's copy if it exists, else the packaged default. |
|
The |
|
|
Classes
|
The |
|
One resolved snippet: its text and where it came from. |
|
The |
|
Everything |
Exceptions
|
- epythet.userconfig.AGENTIC_ASPECTS_POLICIES = ('warn', 'add')¶
What
agentic_aspectsmay be.
- epythet.userconfig.CONFIG_DIR_ENV = 'EPYTHET_CONFIG_DIR'¶
Environment variable overriding the whole config directory.
- epythet.userconfig.CONFIG_FILENAME = 'config.toml'¶
The policy file inside the config directory.
- epythet.userconfig.PACKAGED_SNIPPETS_DIR = PosixPath('/home/runner/work/epythet/epythet/epythet/data/snippets')¶
Where the packaged default snippets live.
- class epythet.userconfig.ReadmePolicy(agentic_aspects='warn', humor=False, agentic_first=False)[source]¶
Bases:
objectThe
[readme]table: what to do about agentic aspects missing from a README.agentic_aspectsis"warn"(report only; the packaged default) or"add"(write or update the section).humordraws the “for humans” line from the humour pool;agentic_firstplaces the section right after the README’s intro rather than at the end.
- epythet.userconfig.SNIPPETS_DIRNAME = 'snippets'¶
The snippet folder inside the config directory (unless
[snippets] dirsays otherwise).
- epythet.userconfig.SNIPPET_COMMANDS = {'diff': <function snippets_diff>, 'init': <function snippets_init>, 'list': <function snippets_list>, 'show': <function snippets_show>}¶
The
epythet snippetsgroup, by command-line name.
- class epythet.userconfig.Snippet(name, path, source, text, copied_from='')[source]¶
Bases:
objectOne resolved snippet: its text and where it came from.
sourceis"user"or"packaged";copied_fromis the epythet version recorded in a user copy’s header (""for a packaged snippet or a user file written by hand).
- class epythet.userconfig.SnippetsConfig(dir='')[source]¶
Bases:
objectThe
[snippets]table:diroverrides where user snippets are read.A relative
diris taken relative to the config directory, so the sameconfig.tomlmeans the same folder from any shell.
- class epythet.userconfig.UserConfig(readme=<factory>, snippets=<factory>, path=None)[source]¶
Bases:
objectEverything
config.tomlcan say, with defaults for what it does not.- readme_for(project_overrides=None)[source]¶
The effective policy for one project:
[tool.epythet.readme]keys override the user’s.Committed READMEs should not depend on who ran the tool, so a project can pin what matters for its text (
humor,agentic_first) in itspyproject.toml;agentic_aspectsmay be pinned too.- Return type:
>>> UserConfig().readme_for({"humor": True}).humor True >>> UserConfig().readme_for({"humour": True}) Traceback (most recent call last): ... UserConfigError: unknown key(s) ['humour'] in [tool.epythet.readme]
- exception epythet.userconfig.UserConfigError[source]¶
Bases:
ConfigErrorconfig.tomlhas a key epythet does not know or a value it cannot use.
- epythet.userconfig.config_dir()[source]¶
$EPYTHET_CONFIG_DIR, else$XDG_CONFIG_HOME/epythet, else~/.config/epythet.XDG-style on every platform, like
epythet.validation.ledger.user_data_dir().- Return type:
>>> _saved = os.environ.get(CONFIG_DIR_ENV) >>> os.environ[CONFIG_DIR_ENV] = "/tmp/x"; config_dir().as_posix() '/tmp/x' >>> _ = os.environ.pop(CONFIG_DIR_ENV) if _saved is None else os.environ.__setitem__(CONFIG_DIR_ENV, _saved)
- epythet.userconfig.diff_snippet(name, *, user_dir=None)[source]¶
A unified diff from the current packaged default to the user’s copy (
""when equal).Headers are ignored, so a freshly
init-ed copy has no diff. A user-only snippet (no packaged default) diffs against nothing, so every line is an addition.- Return type:
- epythet.userconfig.epythet_version()[source]¶
epythet’s version: the checkout’s
pyproject.tomlwhen running from source, else the installed metadata.- Return type:
- epythet.userconfig.header_version(text)[source]¶
The epythet version recorded in a user copy’s header (
""when absent).- Return type:
>>> header_version(snippet_header("x", "0.2.5") + "body") '0.2.5' >>> header_version("no header") ''
- epythet.userconfig.init_snippets(*, user_dir=None, force=False, names=None)[source]¶
Copy the packaged defaults into the user snippet folder; returns the paths written.
A file that already exists is left alone unless
forceis true (then it is replaced;epythet snippets difffirst is the way to see what you lose).
- epythet.userconfig.iter_snippets(*, user_dir=None)[source]¶
Every available snippet, resolved, in name order.
- epythet.userconfig.load_user_config(path=None)[source]¶
Read
config.toml(default:config_path()); a missing file means defaults.- Raises:
UserConfigError – on an unknown table or key, or an invalid value
- Return type:
- epythet.userconfig.packaged_snippet_names()[source]¶
The names of the snippets epythet ships, sorted.
- epythet.userconfig.pool_lines(text)[source]¶
The non-empty, non-comment lines of a pool snippet (one candidate per line).
>>> pool_lines("# a comment\n\nIf you are a control freak\n If you like it \n") ['If you are a control freak', 'If you like it']
- epythet.userconfig.snippet(name, *, user_dir=None)[source]¶
Resolve
name: the user’s<name>.mdwins over the packaged default.
- epythet.userconfig.snippet_header(name, version)[source]¶
The provenance line
initwrites at the top of a user copy.- Return type:
- epythet.userconfig.snippet_names(*, user_dir=None)[source]¶
Every snippet name available: packaged plus user-only files, sorted.
- epythet.userconfig.snippet_text(name, *, user_dir=None)[source]¶
The effective body of
name(header stripped).- Return type:
- epythet.userconfig.snippets_diff(name='')[source]¶
Show how the user’s copy of a snippet differs from the current packaged default.
Without
--name, every user copy that differs is shown. Exit code 1 when any difference exists, likediff, so scripts can tell.- Parameters:
name (
str) – one snippet, or omitted for all
- epythet.userconfig.snippets_dir(config=None)[source]¶
Where user snippets are read:
[snippets] dirif set, else<config dir>/snippets.- Return type:
- epythet.userconfig.snippets_init(*, force=False)[source]¶
Copy the packaged default snippets into the user snippet folder, once.
Each copy starts with a header recording the epythet version it came from. Existing files are never overwritten unless
--forceis given.- Parameters:
force (
bool) – replace existing user copies (rundifffirst to see what you lose)
- epythet.userconfig.snippets_list()[source]¶
List every snippet with its source (user or packaged), provenance and status.
- epythet.userconfig.snippets_show(name)[source]¶
Print the effective text of a snippet: the user’s copy if it exists, else the packaged default.
- Parameters:
name – the snippet name (
epythet snippets listshows them)