Skip to content

Argument suggestions, context falsy/None fix, docs lead with with#184

Merged
lesnik512 merged 2 commits into
mainfrom
ux-fixes-argument-suggestions-context-sentinel-docs
May 30, 2026
Merged

Argument suggestions, context falsy/None fix, docs lead with with#184
lesnik512 merged 2 commits into
mainfrom
ux-fixes-argument-suggestions-context-sentinel-docs

Conversation

@lesnik512
Copy link
Copy Markdown
Member

Summary

Three Tier-1 fixes from plan.md (items #2, #3, #5) — localized, no public API breakage.

#2ArgumentResolutionError runs the suggestion engine

The rich "Did you mean:" hints (subclass / baseclass / typo) previously only fired on ProviderNotRegisteredError. The most common debugging case — "why won't argument db resolve inside this factory?" — got a bare message. Now ArgumentResolutionError accepts an optional suggestions= and appends them the same way. The factory passes providers_registry.build_suggestions(arg_type) at the no-provider-found raise site. The ContextProvider-missing-context raise site is unchanged (suggestions about unrelated types would mislead there).

#3ContextProvider no longer conflates "missing" with "falsy" or "None"

ContextRegistry.find_context used if context_type and (context := self.context.get(...)) which silently dropped legitimate False, 0, "", [], {} values. Fixed via in membership check + types.UNSET sentinel return. The factory's missing-context detection now uses ContextProvider._find_context_value(container) is types.UNSET instead of provider.resolve(container) is None, so context={Key: None} is also a valid value. ContextProvider.resolve() keeps its T | None public contract (translates UNSET → None).

#5 — Docs lead with with / async with

Container.__enter__/__exit__/__aenter__/__aexit__ already exist (container.py:156–166), but the quickstart and integration examples led with try/finally + manual close_sync()/close_async(). Worse, the 2.x migration guide falsely claimed context managers had been removed. Switched all examples to context managers and corrected the migration guide.

Test plan

  • just lint — clean (ruff format + check, ty)
  • just test — 122 passed, 100% coverage
  • New tests in tests/test_suggestions.py: subclass / baseclass / typo / no-match suggestions surface through ArgumentResolutionError
  • New tests in tests/providers/test_context_provider.py: parametrized falsy values round-trip, factory accepts bool=False via context, factory accepts Optional[X] resolving to None via context

🤖 Generated with Claude Code

…one bug, switch docs to context managers

- ArgumentResolutionError now appends the same "Did you mean:" hints
  (subclass/baseclass/typo) that ProviderNotRegisteredError emits, so
  factory-argument resolution failures get the same UX.
- ContextRegistry.find_context now returns a sentinel (types.UNSET) when
  a key is absent instead of conflating absence with falsy values. The
  factory disambiguates via ContextProvider._find_context_value, so
  context={K: None}, False, 0, "", [], {} are all valid context values.
  ContextProvider.resolve keeps its T | None public contract.
- Docs lead with `with` / `async with`. The 2.x migration guide's claim
  that context managers were removed is corrected — they are the
  recommended form; close_sync/close_async remain for manual control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 self-assigned this May 30, 2026
@lesnik512 lesnik512 changed the title Argument suggestions, context falsy/None fix, docs lead with Argument suggestions, context falsy/None fix, docs lead with with May 30, 2026
@lesnik512 lesnik512 merged commit 6497357 into main May 30, 2026
7 checks passed
@lesnik512 lesnik512 deleted the ux-fixes-argument-suggestions-context-sentinel-docs branch May 30, 2026 14:05
lesnik512 added a commit that referenced this pull request May 30, 2026
… value

CacheItem.cache used None both as the 'not cached' sentinel and as a
potentially-valid cached value. Two consequences:

1. A creator that returns None was treated as never-cached: it would
   be re-created on every resolve and its finalizer would never run.
2. The `if self.cache and ...` guard in close_sync/close_async skipped
   finalizers for any cached-but-falsy resource (empty dict, 0, False,
   empty string). Same anti-pattern PR #184 fixed in ContextRegistry.

Switched to types.UNSET as the 'not cached' sentinel. Now:
- A None-returning creator caches once, returns the cached None on
  subsequent resolves, and runs its finalizer at close time.
- Falsy-but-set cached values (empty dict/list/0/False) get their
  finalizers called too.

Touches the four sites that distinguished cached from not-cached:
cache_registry.py (_clear, close_async, close_sync, cached_count) and
factory.py (the two cached-value short-circuit reads in resolve).

Three new regression tests cover: falsy-sync, falsy-async, and the
None-returning creator path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant