FE-1467: Lint and type-check the Python packages in CI - #9261
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryMedium Risk Overview Config lives in each package’s Type checking also drove a few real fixes: Reviewed by Cursor Bugbot for commit 554f741. Bugbot is set up for automated code reviews on this repo. Configure here. |
Adds libs/@local/petrinaut-python: a stdlib-only, POSIX-only package wrapping the CLI's JSON-lines protocol. PetrinautSession serves a model (from a file or over stdin) with healthz/metadata/run plus a generic request method; OptimizationSession adds describe/evaluate/objective over an opaque manifest, and evaluate returns per-seed replicates when the manifest asks for more than one seed. The code comes from petrinaut-opt's client: spawn scrubbing, bounded reads, and process-group shutdown are unchanged. petrinaut-opt consumes the package as an editable uv path dependency, mirrored as a workspace:* edge so Turbo pruning and change detection see it; its codegen inputs, the Docker python-deps/runner stages, and the deploy path filter follow. The old src/petrinaut_client.py, the PYTHON_INTEGRATION.md walkthrough, and examples/python_stdio.py are retired, and MODEL_EXAMPLES.md now uses the bindings. Two end-to-end pytests drive the real built CLI bundle, one per session type.
90e61c1 to
c29486b
Compare
The method sat on a class already named OptimizationSession, while its siblings evaluate() and objective() carry no prefix. describe() maps 1:1 to the protocol's optimization.describe. The previous name remains as a class-level alias. Raised in review on #9228.
Encoding failures happen before anything is written: params or a bootstrap payload that will not serialize to JSON raise TypeError, and a payload over the line cap raises ValueError. Both previously raised PetrinautClientError, whose contract says the process or transport is no longer usable — a caller treating it as terminal would discard a healthy session. The request id is now consumed only after a successful encode, so a rejected request leaves the session exactly as it was. Raised by Cursor and in review on #9228.
ruff and basedpyright are configured in each package's pyproject.toml and pinned as uv dev dependencies, so an editor and CI resolve the same versions with no editor-specific files. New lint:ruff and lint:types tasks are wired into the per-package probe in lint.yml.
c29486b to
554f741
Compare
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1002 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1527 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 108 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 27 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | Flame Graph |
representative_read_entity
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |
Note
Replaces #9260, which GitHub's stack automation marked as merged during a stack reorder. Same commits, same branch; review history lives on the old PR.
🌟 What is the purpose of this PR?
The two Python packages had no linter, no formatter, and no type checker; CI ran their pytest suites and nothing else. This PR configures ruff and basedpyright in both packages and runs them in CI.
There are no editor-specific files: every editor reads the configuration from
pyproject.toml, and both tools are pinned as uv dev dependencies so an editor and CI resolve the same versions fromuv.lock.Sits on FE-1270 (#9228) in stack #9269; FE-1468 (#9262) is above.
🔗 Related links
🔍 What does this change?
Configuration, in each package's
pyproject.toml:E, F, I, UP, B, SIM, RUF, TRY, DTZ, PTHand formats withruff format.typeCheckingMode = "standard"oversrc. FE-1468 raises it tostrict.TRY003(error messages here name the value that failed),TRY004(a non-conforming protocol response raisesValueError, notTypeError), andE501(ruff formatowns line width; it cannot split long strings or the single-line@roleannotation).Four bugs the type checker found:
telemetry.pyannotated its exporters asLogExporter, which the OTLP exporters do not implement; the runtime base isLogRecordExporter.__enter__returnedPetrinautSession, sowith OptimizationSession(...) as sessionlost the optimization methods. It now returns the calling type.petrinaut_optimizer.pyshadowed itscallbackparameter with a nested function of the same name.OptimizationSession.__init__passedMapping | NonewhereMappingwas required; the branches now narrow it.One root change:
!**/.venvjoins the workspace globs inpackage.json. basedpyright bundles a Node binary, so itspackage.jsoninside each.venvwas discovered as a yarn workspace and the two copies broke Turborepo entirely.CI:
lint:ruffandlint:typesscripts in both packages, declared in the rootturbo.json, detected by the per-package task check inlint.yml, which reportsfalsefor every non-Python package.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
pyproject.toml.🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
turbo.json's have been updated to reflect this:lint:ruffandlint:typesare declared with no dependencies, sinceuv runsyncs each package's environment from its own lockfile.🛡 What tests cover this?
turbo run lint:ruff lint:typespasses for both packages — exactly what the new CI steps run.❓ How to test this?
turbo run lint:ruff lint:types --filter @local/petrinaut-python --filter @apps/petrinaut-optapps/petrinaut-opt/src/telemetry.pyin an editor with the ruff and basedpyright language servers: diagnostics appear with no editor configuration in the repository.🤖 Generated with Claude Code