Skip to content

Routes live in client.routes; a route name cannot replace a client attribute (#23) - #24

Merged
thorwhalen merged 2 commits into
masterfrom
fix/23-route-namespace
Oct 13, 2026
Merged

thorwhalen merged 2 commits into
masterfrom
fix/23-route-namespace

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Closes #23.

HttpClient.register_method did setattr(self, name, ...) with the spec's x-method_name, so a spec (possibly served by a remote) could replace openapi_spec, session, handle_request, ensure_login, ...

Change

  • Every route is in client.routes ({name: bound method}).
  • A route is also set as an attribute when its name is public and is not already a client attribute (class attribute or instance attribute). Otherwise a warning points to client.routes[name].
  • title, version, base_url (set in __init__, never read afterwards) can still be taken by a route, so py2http services with a function named version keep working, as do packages generated before this change.
  • api_pkg_maker and cli_maker enumerate client.routes; the CLI and generated packages call routes[name], so a route named like a client attribute works there.
  • Duplicate route names now warn (the later replaces the earlier, as before).

Adversarial review (fresh Opus subagent): first verdict FIX-FIRST. Confirmed and fixed in the second commit: the CLI still dispatched by getattr (a colliding route ran the client attribute); title/version routes would break existing generated packages and dependents using getattr(api, name) (now shadowable); hasattr refused every route for a __getattr__ subclass; a subclass skipping __init__ had no routes; warnings pointed into http2py (stacklevel); is_request_method disagreed with attribute access; silent duplicate-name loss. Not changed: no version floor added to generated packages' install_requires (a package generated by this version against an older http2py fails on import; generation and runtime normally share a version). Checked safe: {func_name!r} in the generated source (names are validated identifiers first).

Tests: tests/test_route_namespace.py (fail on master). Dependents gate: see comment.

🤖 Generated with Claude Code

thorwhalen and others added 2 commits October 13, 2026 05:24
…nt attribute (#23)

register_method did setattr(self, name, ...) with the spec's x-method_name, so
a (possibly remote) spec could replace openapi_spec, session, handle_request...
Every route now goes in client.routes; it is also set as an attribute only when
its name is public and not already an attribute (a later route of the same name
still replaces an earlier one). Otherwise a warning points to client.routes.
api_pkg_maker and cli_maker enumerate client.routes, and generated packages call
api.routes[name], so a route named like a client attribute still works there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the CLI calls client.routes[name], not getattr (a route named like a client
  attribute ran the attribute)
- title/version/base_url (never read after __init__) can still be route names,
  so existing clients and generated packages with such routes keep working
- collisions are checked on the class and instance dict, not hasattr (a
  __getattr__ subclass refused every route); routes is created lazily for a
  subclass that skips HttpClient.__init__
- duplicate route names warn; warnings point at the caller (stacklevel=3);
  is_request_method looks at the attribute again

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thorwhalen

Copy link
Copy Markdown
Member Author

Dependents gate (priv test-dependents http2py --transitive, branch): http2py, extrude, py2http — 3 pass, 0 fail.

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.

HttpClient: a route's x-method_name can overwrite the client's own attributes

1 participant