Skip to content

fix: accept 'pkg.mod:name' colon refs; warn on local-path leaks in golden recordings - #44

Merged
thorwhalen merged 1 commit into
masterfrom
fix/colon-refs-and-local-path-warning
Sep 22, 2026
Merged

thorwhalen merged 1 commit into
masterfrom
fix/colon-refs-and-local-path-warning

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Summary

  • resolve_to_function / resolve_func_from_dot_path / parse_spec_with_dot_path now also accept the 'pkg.mod:name' colon form that commands_from and mk_parser's obj: already document elsewhere in cw, delegating to cw.commands.import_object (the one existing implementation of that grammar) when a colon is present. The plain dot-path grammar is unchanged and still works exactly as before — this only widens what's accepted; no previously-valid input becomes invalid. Closes part 2 of Two resolution/naming inconsistencies: hyphenate_groups ignored by add_commands(group_name=), and resolve_to_function rejects the documented 'pkg.mod:name' form #40.
  • cw.testing: new local_path_hits() helper plus a characterize(..., warn_on_local_paths=True) opt-out flag that warns (UserWarning, doesn't rewrite anything) when a recorded golden's --help body embeds an absolute path under the recording machine's home directory — argparse routinely renders $HOME-derived defaults into --help, and a golden is meant to be committed. Closes cw.testing: recorded --help bodies can embed the recording user's home directory, silently #38.
  • Neither existing public name's default behaviour changed for previously-valid calls: the new characterize parameter defaults to True (a new warning surfaces only when a golden genuinely does carry a local path — nothing about non-offending callers changes), and the accepted-input grammar only grows.

Does not touch #40's first item (hyphenate_groups ignored by add_commands(group_name=...)) — that's a separate, larger behaviour-change question (verbatim vs. hyphenated) left for its own PR. Referencing, not closing, #40.

Dependents check (cw has ~40 fleet dependents per fleet_dependents.json)

Grepped the whole fleet for callers of the touched surface (resolve_to_function, parse_spec_with_dot_path, characterize, local_path_hits) and ran each hit's relevant test file against this branch installed editable (not PyPI cw) in an isolated venv:

  • citeget, isee, lookbook, ov, scraped, theremin: green (isee's two unrelated failures were a venv missing pip, fixed by installing it — nothing to do with this change).
  • priv: its CLI-parity tests skip in this sandbox (the local .pth ecosystem is not importable) — pre-existing environment limitation, unaffected by this branch.
  • wads: covered separately below (also a target of this landing batch) — its own suite is green against this branch.
  • discorddol, gurgle, enlace_auth (present on box under nested dirs) do not call the touched API at all.

Test plan

  • wads ci-local (private-Actions-blocked account, priv#139): ruff format + lint, pytest on py3.10/py3.12, build — all green (680 passed, 10 skipped, 1 warning).
  • Not covered by the local gate: Windows matrix, any Linux-only job, secrets-dependent jobs (none apply to this change).

🤖 Generated with Claude Code

…a golden records a home path

Two independent fixes, both additive.

resolve_to_function rejected the colon reference (#40, part 2)
--------------------------------------------------------------
`'pkg.mod:name'` is the house spelling -- `commands_from`, `import_object`,
`mk_parser`'s `obj:` doc and `python -m cw` all document it and say the colon
is required -- but `cw.resolve_to_function`, the exported and most generically
named resolver, raised `ValueError` on it. Two public "turn a string into a
function" entry points accepted different grammars, and the stricter one was
the one people reach for first.

- `parse_spec_with_dot_path` now validates against `_DOT_OR_COLON_REF`
  (`^[\w.]+(?::[\w.]+)?$`), built from `commands.REF_SEPARATOR` so there is one
  spelling of the grammar. Two colons are still refused, and the error names
  both accepted forms while keeping the phrase existing tests match on.
- `resolve_func_from_dot_path` delegates a colon reference to
  `commands.import_object` -- one implementation of the import -- and keeps the
  existing `callable()` check and the `(ImportError, AttributeError) ->
  ValueError` wrapping, so the failure shape is unchanged.

Pure widening: every string accepted before resolves to the identical object,
and the only delta is that strings which used to raise now succeed. The one
observable shift is with a Mapping `get_func`, where a colon spec moves from
`ValueError` to the `TypeError` a Mapping already raises for *any* unknown key
-- so a colon reference stops being singled out by the parser and behaves like
every other key. The single fleet dependent is green.

characterize() recorded home directory paths silently (#38, fix 3 only)
-----------------------------------------------------------------------
`characterize`'s own docstring says to commit the golden, and argparse renders
parameter defaults into `--help`, so a body routinely froze an absolute path
under the recording user's home into a public repo. Nothing caught it: a
`--help` body is tier 3, so it is a snapshot and never asserted, and `replay`
reports `identical` on every machine. Four repos in one migration wave had to
drop the case from the corpus after noticing by hand.

- `local_path_hits(text, *, home='~')` reports which markers a body carries
  (the running user's home, then the generic `HOME_ROOTS`). It reports rather
  than scrubs, because only the caller knows what belongs in the path's place.
- `characterize(..., warn_on_local_paths=True)` warns at record time, naming
  the offending argv. Recorded content is byte-identical either way, and
  nothing near `_record`, the golden schema or the compare path is touched.

Deliberately excluded: the `redact=` half of the original proposal, which has
to be persisted in the golden and reapplied by `replay` to be correct, and
`hyphenate_groups` from #40 part 1, which renames a live subcommand.

`warnings` is imported inside the one function that needs it, so testing.py's
D4 standalone contract (its module-scope imports are an asserted set) holds.
The existing corpus produces zero hits, so the default is silent today.

Tests: 952 passed, 2 skipped -> 973 passed, 2 skipped. Parity gate still
"8 shapes / 137 cases: identical".

Claude-Session: https://claude.ai/code/session_01L1aQPB34n7PU7jmbztSjBe
@thorwhalen
thorwhalen merged commit e719cce into master Sep 22, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the fix/colon-refs-and-local-path-warning branch September 22, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cw.testing: recorded --help bodies can embed the recording user's home directory, silently

1 participant