Load a TaskEnv from a config file with TaskEnv.from_config (v0.1.17) - #60
Merged
Volv-G merged 1 commit intoSep 19, 2026
Conversation
Adds a generic `TaskEnv.from_config(path)` so an execution environment can be declared in a YAML/JSON file instead of hardcoded in the pipeline module. Every `TaskEnv` dataclass subclass inherits it, returns its own type, and is validated against its own fields. - A relative path resolves against the calling file, never the working directory, so a pipeline selects the same config wherever the CLI runs. - The document is read with `ArgsContainer._load_config_file`, the loader `--config` already uses, so `_select` and the accepted document shapes cannot drift from the CLI. It must resolve to exactly one object. - Accepted keys come from `inspect.signature(cls)` rather than `dataclasses.fields()`, which is what `cls(**case)` actually accepts: InitVar pseudo-fields in, ClassVar and `field(init=False)` out. Unknown keys are rejected with the allowed names, keeping these files environment-only. - A relative `dependencies_from` anchors to the config file's directory. A config file is untrusted input, so no diagnostic echoes a config value. Keys go through the existing capped, control-character-scrubbing renderer, now widened to accept non-string YAML keys and applied to the `_select` sibling/unexpected paths that previously used bare `repr()`. A constructor's own exception text is never quoted, since it may embed a value directly, nested inside a structure, or transformed; the rejected exception is also kept off `__cause__`/`__context__`, which a rendered traceback would otherwise print into the same CI log. The message names the class and the fields present and points the author at constructing the class directly, which is unchanged and still raises the ordinary `ValueError`. `TaskEnv.from_config` is the only new public name. Assisted-By: devx/bc7bacf9-958c-4255-ab2f-d2d5b96647b8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(AI-assisted)
Adds a generic
TaskEnv.from_config(path)so an execution environment can be declared in a YAML/JSON file instead of hardcoded in the pipeline module. EveryTaskEnvdataclass subclass inherits it, returns its own type, and is validated against its own fields.TaskEnv.from_configis the only new public name in this PR.Behaviour
pathresolves against the calling file's directory, never the working directory, so a pipeline script selects the same config wherever the CLI is run from. No__file__(exec/REPL) is a clear error, never a silent CWD fallback.ArgsContainer._load_config_file— the loader--configalready uses — so the_selectdirective and the accepted document shapes cannot drift from the CLI. It must resolve to exactly one object.inspect.signature(cls)rather thandataclasses.fields(), because that is whatcls(**case)actually accepts:InitVarpseudo-fields in,ClassVarandfield(init=False)out.*args/**kwargsare excluded so a typo stays fail-closed. Unknown keys are rejected with the allowed names, which keeps these files environment-only — pipeline concerns like versioning or schedules have no field to land in.dependencies_fromin the object anchors to the config file's directory rather than the module's.Diagnostics never echo a config value
A config file is untrusted input, so nothing from it reaches a compile/CI log.
Keys go through the existing capped, control-character-scrubbing renderer, widened to accept non-string YAML keys and now applied to the
_selectsibling/unexpected paths that previously used barerepr().A constructor's own
TypeError/ValueErrortext is never quoted — it may embed a value directly, as a leaf nested inside a structure, or transformed (lower cased, sliced, re-encoded), and no content scan can undo a transformation. The rejected exception is also kept off__cause__and__context__, becausetraceback.format_exceptionprints a chain verbatim; the error is raised after leaving theexcepthandler, sinceraise ... from Nonewould still leave the object reachable on__context__.Calling
GpuEnv(...)directly is unaffected and raises the ordinaryValueErrorin full.Tests
tests/test_task_env_from_config.py— 29 tests: caller-relative and absolute resolution, config-file-relativedependencies_from,_selectwith and without a default, subclass field/type inheritance,InitVar/ClassVar/init=Falseacceptance, environment-only rejection, and an end-to-end compile of a generated task. Disclosure is covered for raw, nested-leaf, transformed, and single-character values, asserted against the rendered traceback rather than juststr(exc). Reintroducing the exception chain fails 5 of them.Full suite: 1540 passed.
git diff --checkclean.Version
0.1.16→0.1.17across the four surfaces:pyproject.toml,packages/tangle-cli/src/tangle_cli/__init__.py,tests/test_packaging.py, and the editable self-entry inuv.lock(hand-edited,1 1diff; the lockfile was not regenerated).