From cde9c6d253740a73dfbeaef21711346b92856cc5 Mon Sep 17 00:00:00 2001 From: Mike Arpaia Date: Sat, 26 Sep 2026 08:09:08 -0600 Subject: [PATCH] Fix CI lint failures and refresh API reference --- .github/workflows/ci.yml | 4 +- .pre-commit-config.yaml | 6 +-- docs/API_Reference.rst | 8 ++++ pyproject.toml | 7 ++- setup.py | 2 +- .../adapters/opentrons/simulation.py | 4 +- src/buildcompiler/adapters/pudu/__init__.py | 4 +- .../adapters/pudu/assembly_json.py | 2 +- .../adapters/pudu/transformation_json.py | 1 - src/buildcompiler/api/__init__.py | 12 ++--- src/buildcompiler/api/compiler.py | 10 ++-- src/buildcompiler/execution/executor.py | 4 +- src/buildcompiler/inventory/__init__.py | 4 +- src/buildcompiler/inventory/indexing.py | 2 +- src/buildcompiler/inventory/inventory.py | 1 - src/buildcompiler/inventory/selector.py | 7 ++- src/buildcompiler/planning/__init__.py | 4 +- src/buildcompiler/planning/combinatorial.py | 7 ++- src/buildcompiler/planning/validation.py | 2 +- src/buildcompiler/sbol/__init__.py | 2 +- src/buildcompiler/sbol/assembly.py | 6 ++- src/buildcompiler/sbol/domestication.py | 12 ++--- src/buildcompiler/sbol/resolver.py | 3 +- src/buildcompiler/sbol/synbiohub.py | 6 +-- src/buildcompiler/stages/assembly_lvl2.py | 6 ++- tests/conftest.py | 2 +- .../integration/test_full_build_happy_path.py | 2 +- .../test_legacy_offline_fixtures.py | 1 - .../test_missing_lvl1_then_domestication.py | 2 +- .../test_offline_quickstart_notebook.py | 1 - tests/unit/api/test_synbiohub_factory.py | 7 +-- tests/unit/planning/test_combinatorial.py | 5 +- .../planning/test_domestication_planner.py | 8 ++-- tests/unit/planning/test_validation.py | 3 +- tests/unit/sbol/test_assembly_service.py | 2 +- tests/unit/sbol/test_resolver.py | 2 +- tests/unit/stages/test_assembly_lvl2.py | 2 +- tests/unit/test_core_imports.py | 14 +++--- uv.lock | 47 +++++++++---------- 39 files changed, 122 insertions(+), 102 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4754fa7..b3e8665 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: core: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: python-version: ["3.10", "3.11"] steps: @@ -21,8 +22,7 @@ jobs: - name: Install package run: | python -m pip install -U pip - python -m pip install -e ".[test]" - python -m pip install ruff + python -m pip install -e ".[dev]" - name: Ruff check run: >- ruff check diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e56bb19..1fe4320 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,9 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.16.8 hooks: # Ruff Linter - - id: ruff + - id: ruff-check args: [ --fix ] - - id: ruff-format \ No newline at end of file + - id: ruff-format diff --git a/docs/API_Reference.rst b/docs/API_Reference.rst index 47d0f68..b4d3737 100644 --- a/docs/API_Reference.rst +++ b/docs/API_Reference.rst @@ -262,6 +262,14 @@ buildcompiler.domain.plasmid :undoc-members: :show-inheritance: +buildcompiler.domain.protocol +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.protocol + :members: + :undoc-members: + :show-inheritance: + buildcompiler.domain.reagent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/pyproject.toml b/pyproject.toml index 2719f5b..f899f9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ test = [ dev = [ "pytest>=7,<9", "pytest-cov[all]", - "ruff>=0.14.0", + "ruff==0.16.8", "build>=1.2", "twine>=5.0", ] @@ -72,9 +72,12 @@ where = ["src"] [dependency-groups] dev = [ - "ruff>=0.14.0", + "ruff==0.16.8", ] +[tool.ruff] +required-version = "==0.16.8" + [tool.pytest.ini_options] markers = [ diff --git a/setup.py b/setup.py index 0bcf9e5..32f7cf0 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "dev": [ "pytest>=7,<9", "pytest-cov[all]", - "ruff>=0.14.0", + "ruff==0.16.8", "build>=1.2", "twine>=5.0", ], diff --git a/src/buildcompiler/adapters/opentrons/simulation.py b/src/buildcompiler/adapters/opentrons/simulation.py index 1fe66db..0f5becd 100644 --- a/src/buildcompiler/adapters/opentrons/simulation.py +++ b/src/buildcompiler/adapters/opentrons/simulation.py @@ -2,10 +2,10 @@ from __future__ import annotations -from dataclasses import dataclass, field -from pathlib import Path import shutil import subprocess +from dataclasses import dataclass, field +from pathlib import Path from buildcompiler.api import ProtocolOptions diff --git a/src/buildcompiler/adapters/pudu/__init__.py b/src/buildcompiler/adapters/pudu/__init__.py index 242d8f4..0dbcb23 100644 --- a/src/buildcompiler/adapters/pudu/__init__.py +++ b/src/buildcompiler/adapters/pudu/__init__.py @@ -23,9 +23,9 @@ "domestication_artifacts_to_pudu_json", "legacy_assembly_route_to_pudu_json", "legacy_assembly_routes_to_pudu_json", - "write_assembly_pudu_input_json", "plasmid_locations_to_pudu_json", + "plating_to_pudu_json", "transformation_to_pudu_json", "transformations_to_pudu_json", - "plating_to_pudu_json", + "write_assembly_pudu_input_json", ] diff --git a/src/buildcompiler/adapters/pudu/assembly_json.py b/src/buildcompiler/adapters/pudu/assembly_json.py index 02b00dc..00aad4a 100644 --- a/src/buildcompiler/adapters/pudu/assembly_json.py +++ b/src/buildcompiler/adapters/pudu/assembly_json.py @@ -1,7 +1,7 @@ """In-memory adapter for compiler-level PUDU assembly JSON payloads.""" -from collections.abc import Sequence import json +from collections.abc import Sequence from pathlib import Path from typing import Any diff --git a/src/buildcompiler/adapters/pudu/transformation_json.py b/src/buildcompiler/adapters/pudu/transformation_json.py index 4678895..15017b0 100644 --- a/src/buildcompiler/adapters/pudu/transformation_json.py +++ b/src/buildcompiler/adapters/pudu/transformation_json.py @@ -4,7 +4,6 @@ from buildcompiler.domain import IndexedPlasmid, TransformationProtocolSpec - PUDU_96_WELL_ORDER = tuple( f"{row}{column}" for column in range(1, 13) for row in "ABCDEFGH" ) diff --git a/src/buildcompiler/api/__init__.py b/src/buildcompiler/api/__init__.py index ae81ed8..f3f4e00 100644 --- a/src/buildcompiler/api/__init__.py +++ b/src/buildcompiler/api/__init__.py @@ -57,9 +57,10 @@ ) __all__ = [ + "SCHEMA_VERSION", + "ApprovalOptions", "BuildCompiler", "BuildCompilerError", - "ApprovalOptions", "BuildOptions", "CombinatorialOptions", "DomesticationOptions", @@ -71,8 +72,6 @@ "ReagentOptions", "ReportingOptions", "SelectionOptions", - "TransformationOptions", - "SCHEMA_VERSION", "SerializationError", "SynBioHubAuthenticationError", "SynBioHubConfigurationError", @@ -80,14 +79,14 @@ "SynBioHubNetworkError", "SynBioHubResourceError", "SynBioHubResponseError", + "TransformationOptions", "assembly_lvl1", "assembly_lvl2", + "deserialize_build_plan", "domestication", + "dumps_json_dto", "full_build", "index_collections", - "transformation", - "deserialize_build_plan", - "dumps_json_dto", "serialize_approval", "serialize_backbone", "serialize_build_plan", @@ -103,4 +102,5 @@ "serialize_unsupported_planning_record", "serialize_warning", "to_json_dto", + "transformation", ] diff --git a/src/buildcompiler/api/compiler.py b/src/buildcompiler/api/compiler.py index de11568..82cc9f6 100644 --- a/src/buildcompiler/api/compiler.py +++ b/src/buildcompiler/api/compiler.py @@ -2,8 +2,8 @@ from __future__ import annotations -from dataclasses import dataclass, field from collections.abc import Sequence +from dataclasses import dataclass, field from typing import Any import sbol2 @@ -43,7 +43,7 @@ def from_synbiohub( auth_token: str | None = None, sbol_doc: sbol2.Document | None = None, options: BuildOptions | None = None, - ) -> "BuildCompiler": + ) -> BuildCompiler: """Create a compiler from token-authenticated SynBioHub collections. The token is assigned only to a transient ``sbol2.PartShop`` while the @@ -321,7 +321,8 @@ def _resolve_component( value if isinstance(value, sbol2.ComponentDefinition) else document.find(value) ) if not isinstance(component, sbol2.ComponentDefinition): - raise ValueError(f"ComponentDefinition not found: {value}") + # Invalid document references use the public ValueError contract. + raise ValueError(f"ComponentDefinition not found: {value}") # noqa: TRY004 return component @@ -332,5 +333,6 @@ def _resolve_module( value if isinstance(value, sbol2.ModuleDefinition) else document.find(value) ) if not isinstance(module, sbol2.ModuleDefinition): - raise ValueError(f"ModuleDefinition not found: {value}") + # Invalid document references use the public ValueError contract. + raise ValueError(f"ModuleDefinition not found: {value}") # noqa: TRY004 return module diff --git a/src/buildcompiler/execution/executor.py b/src/buildcompiler/execution/executor.py index d1f4d74..0f8eab4 100644 --- a/src/buildcompiler/execution/executor.py +++ b/src/buildcompiler/execution/executor.py @@ -70,7 +70,7 @@ def from_dependencies( logger: Any = None, resolver: SbolResolver | None = None, **stage_overrides: Any, - ) -> "FullBuildExecutor": + ) -> FullBuildExecutor: active_resolver = resolver or SbolResolver(sbol_document) return cls( context=BuildContext( @@ -203,7 +203,7 @@ def execute( stage_results=stage_results, options=self.context.options.protocol, ) - except Exception as exc: + except Exception as exc: # noqa: BLE001 - Report backend failures as stage results. stage_results.append( StageResult( id="protocol:bundle", diff --git a/src/buildcompiler/inventory/__init__.py b/src/buildcompiler/inventory/__init__.py index cdf558e..bc4aa3a 100644 --- a/src/buildcompiler/inventory/__init__.py +++ b/src/buildcompiler/inventory/__init__.py @@ -1,16 +1,16 @@ """Inventory package exports for deterministic lookup/indexing contracts.""" from .compatibility import Lvl1Route, Lvl2Route, RouteScore, RouteSelection -from .inventory import Inventory from .indexing import index_collections +from .inventory import Inventory from .selector import CompatibilitySelector __all__ = [ "CompatibilitySelector", "Inventory", - "index_collections", "Lvl1Route", "Lvl2Route", "RouteScore", "RouteSelection", + "index_collections", ] diff --git a/src/buildcompiler/inventory/indexing.py b/src/buildcompiler/inventory/indexing.py index 7202498..6351b58 100644 --- a/src/buildcompiler/inventory/indexing.py +++ b/src/buildcompiler/inventory/indexing.py @@ -273,7 +273,7 @@ def _insert_identities( identities = { child.identity for child in _children(component, document) - if insert_roles & set(str(role) for role in child.roles) + if insert_roles & {str(role) for role in child.roles} } return sorted(identities) diff --git a/src/buildcompiler/inventory/inventory.py b/src/buildcompiler/inventory/inventory.py index 7de5922..06c0bdb 100644 --- a/src/buildcompiler/inventory/inventory.py +++ b/src/buildcompiler/inventory/inventory.py @@ -12,7 +12,6 @@ MaterialState, ) - _MATERIAL_ORDER = { MaterialState.PLANNED: 0, MaterialState.GENERATED: 1, diff --git a/src/buildcompiler/inventory/selector.py b/src/buildcompiler/inventory/selector.py index 4eaf93f..f0fa4f8 100644 --- a/src/buildcompiler/inventory/selector.py +++ b/src/buildcompiler/inventory/selector.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Mapping, Sequence -from itertools import permutations, product +from itertools import pairwise, permutations, product from typing import Any from buildcompiler.api.options import BuildOptions @@ -16,7 +16,6 @@ ) from buildcompiler.inventory.inventory import Inventory - _STATE_RANK = { MaterialState.PLANNED: 0, MaterialState.GENERATED: 1, @@ -73,7 +72,7 @@ def _key(p: Any) -> tuple[int, int, str]: state_penalty = -_STATE_RANK[p.state] if prefer_state else 0 return (generated_penalty, state_penalty, p.identity) - return sorted(filtered, key=_key)[0] + return min(filtered, key=_key) def _candidate_key(self, candidate: Any) -> tuple[int, int, str]: prefer_existing = self.options.selection.prefer_existing_collection_material @@ -93,7 +92,7 @@ def _chain_is_compatible(self, chain: Sequence[Any]) -> bool: return True if any(len(sites) != 2 for sites in annotated): return False - return all(left[1] == right[0] for left, right in zip(annotated, annotated[1:])) + return all(left[1] == right[0] for left, right in pairwise(annotated)) def _select_compatible_chain( self, candidate_groups: Sequence[list[Any]] diff --git a/src/buildcompiler/planning/__init__.py b/src/buildcompiler/planning/__init__.py index 61c009f..27152f2 100644 --- a/src/buildcompiler/planning/__init__.py +++ b/src/buildcompiler/planning/__init__.py @@ -11,10 +11,10 @@ __all__ = [ "BuildPlan", - "UnsupportedPlanningRecord", - "FullBuildPlanner", "DomesticationPlan", "DomesticationPlanner", + "FullBuildPlanner", "SequenceEditProposal", + "UnsupportedPlanningRecord", "select_deterministic_flanking_sequence", ] diff --git a/src/buildcompiler/planning/combinatorial.py b/src/buildcompiler/planning/combinatorial.py index 101ec02..f8592e8 100644 --- a/src/buildcompiler/planning/combinatorial.py +++ b/src/buildcompiler/planning/combinatorial.py @@ -1,6 +1,9 @@ from __future__ import annotations + import itertools + import sbol2 + from buildcompiler.api.options import BuildOptions from buildcompiler.domain import BuildRequest, BuildStage, BuildWarning, DesignKind from buildcompiler.planning.classifier import request_id_for @@ -13,7 +16,7 @@ def _collect_variant_sets(derivation): variables.sort( key=lambda variable: (str(getattr(variable, "variable", "")), variable.identity) ) - return variables, [sorted(list(vc.variants), key=str) for vc in variables] + return variables, [sorted(vc.variants, key=str) for vc in variables] def expand_combinatorial_derivation( @@ -22,7 +25,7 @@ def expand_combinatorial_derivation( warnings = [] unsupported = [] requests = [] - variables, variant_sets = _collect_variant_sets(derivation) + _variables, variant_sets = _collect_variant_sets(derivation) if not variant_sets or any(len(v) == 0 for v in variant_sets): unsupported.append( UnsupportedPlanningRecord( diff --git a/src/buildcompiler/planning/validation.py b/src/buildcompiler/planning/validation.py index b00c70e..1d29b47 100644 --- a/src/buildcompiler/planning/validation.py +++ b/src/buildcompiler/planning/validation.py @@ -97,7 +97,7 @@ def ordered_lvl1_parts( try: ordered_components = list(component_definition.getInSequentialOrder()) - except Exception: + except Exception: # noqa: BLE001 - Fall back to role order if SBOL ordering fails. ordered_components = [] if len(ordered_components) == 4: diff --git a/src/buildcompiler/sbol/__init__.py b/src/buildcompiler/sbol/__init__.py index 0fe6149..98cf1f3 100644 --- a/src/buildcompiler/sbol/__init__.py +++ b/src/buildcompiler/sbol/__init__.py @@ -23,8 +23,8 @@ "DomesticationService", "PullPolicy", "SbolResolver", - "load_synbiohub_collections", "TransformationJob", "TransformationSbolResult", "TransformationService", + "load_synbiohub_collections", ] diff --git a/src/buildcompiler/sbol/assembly.py b/src/buildcompiler/sbol/assembly.py index 3fbb854..951a622 100644 --- a/src/buildcompiler/sbol/assembly.py +++ b/src/buildcompiler/sbol/assembly.py @@ -130,7 +130,8 @@ def _component_from_record( f"Missing SBOL ComponentDefinition for {field_name} record {record.identity}" ) if not isinstance(component, sbol2.ComponentDefinition): - raise ValueError( + # Preserve the ValueError contract for invalid record references. + raise ValueError( # noqa: TRY004 f"{field_name} record {record.identity} must resolve to sbol2.ComponentDefinition" ) return component @@ -166,7 +167,8 @@ def _implementation_from_record( ) implementation = source_document.find(impl_identity) if not isinstance(implementation, sbol2.Implementation): - raise ValueError( + # A missing or invalid document reference is a record value error. + raise ValueError( # noqa: TRY004 "Missing SBOL Implementation for reagent " f"{record.identity}; expected metadata['implementation_identity'] or identity to resolve" ) diff --git a/src/buildcompiler/sbol/domestication.py b/src/buildcompiler/sbol/domestication.py index 14bdf06..499f066 100644 --- a/src/buildcompiler/sbol/domestication.py +++ b/src/buildcompiler/sbol/domestication.py @@ -2,8 +2,8 @@ from __future__ import annotations -from dataclasses import dataclass, field import hashlib +from dataclasses import dataclass, field from typing import Any import sbol2 @@ -16,7 +16,6 @@ MaterialState, ) - ROLE_TO_FUSION_SITE_SEQUENCES = { "promoter": ("GGAG", "TACT"), "rbs": ("TACT", "AATG"), @@ -163,7 +162,8 @@ def run(self, job: DomesticationJob) -> DomesticationSbolResult: def _ensure_component(self, component: Any) -> sbol2.ComponentDefinition: if not isinstance(component, sbol2.ComponentDefinition): - raise ValueError( + # Preserve the existing ValueError contract for invalid jobs. + raise ValueError( # noqa: TRY004 "DomesticationJob.part_component must be an sbol2.ComponentDefinition" ) return component @@ -185,9 +185,9 @@ def _apply_sequence_edit_proposals( key=lambda item: int(getattr(item, "position", 0)), reverse=True, ): - position = int(getattr(proposal, "position")) - original = str(getattr(proposal, "original_sequence")).upper() - proposed = str(getattr(proposal, "proposed_sequence")).upper() + position = int(proposal.position) + original = str(proposal.original_sequence).upper() + proposed = str(proposal.proposed_sequence).upper() if sequence[position : position + len(original)] != original: raise ValueError( "Sequence edit proposal does not match source sequence at " diff --git a/src/buildcompiler/sbol/resolver.py b/src/buildcompiler/sbol/resolver.py index 3618983..f171929 100644 --- a/src/buildcompiler/sbol/resolver.py +++ b/src/buildcompiler/sbol/resolver.py @@ -2,8 +2,9 @@ from __future__ import annotations +from collections.abc import Callable from enum import Enum -from typing import Any, Callable +from typing import Any import sbol2 diff --git a/src/buildcompiler/sbol/synbiohub.py b/src/buildcompiler/sbol/synbiohub.py index b11f4e6..beab6aa 100644 --- a/src/buildcompiler/sbol/synbiohub.py +++ b/src/buildcompiler/sbol/synbiohub.py @@ -29,7 +29,7 @@ def load_synbiohub_collections( try: shop = sbol2.PartShop(sbh_registry) shop.key = auth_token - except Exception as exc: + except Exception as exc: # noqa: BLE001 - Sanitize all client errors to protect tokens. raise _normalized_error( exc, operation="client initialization", @@ -93,7 +93,7 @@ def _pull( ) -> None: try: shop.pull(identity, document) - except Exception as exc: + except Exception as exc: # noqa: BLE001 - Sanitize all download errors to protect tokens. raise _normalized_error( exc, operation=operation, @@ -141,7 +141,7 @@ def _sbol_error_code(exc: Exception) -> Any: return None try: return exc.error_code() - except Exception: + except Exception: # noqa: BLE001 - Unreadable SDK codes fall back to message matching. return None diff --git a/src/buildcompiler/stages/assembly_lvl2.py b/src/buildcompiler/stages/assembly_lvl2.py index 3b8259e..c0f214e 100644 --- a/src/buildcompiler/stages/assembly_lvl2.py +++ b/src/buildcompiler/stages/assembly_lvl2.py @@ -84,8 +84,10 @@ def run( request_ids=[request.id], protocol_artifacts=artifacts, logs=[ - "No lvl2 route selected by CompatibilitySelector. Provide explicit region_order " - "or enable large-order search for large designs.", + ( + "No lvl2 route selected by CompatibilitySelector. Provide explicit region_order " + "or enable large-order search for large designs." + ), ], ) diff --git a/tests/conftest.py b/tests/conftest.py index 18e616f..1be37d7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ from __future__ import annotations -import sbol2 import pytest +import sbol2 from buildcompiler.api import BuildOptions from buildcompiler.domain import BuildRequest, BuildStage, DesignKind diff --git a/tests/integration/test_full_build_happy_path.py b/tests/integration/test_full_build_happy_path.py index bc31584..e75d249 100644 --- a/tests/integration/test_full_build_happy_path.py +++ b/tests/integration/test_full_build_happy_path.py @@ -13,8 +13,8 @@ StageStatus, ) from buildcompiler.execution import BuildContext, FullBuildExecutor -from buildcompiler.planning import BuildPlan from buildcompiler.inventory import Inventory +from buildcompiler.planning import BuildPlan from buildcompiler.sbol import SbolResolver diff --git a/tests/integration/test_legacy_offline_fixtures.py b/tests/integration/test_legacy_offline_fixtures.py index 1022a4e..7badd6f 100644 --- a/tests/integration/test_legacy_offline_fixtures.py +++ b/tests/integration/test_legacy_offline_fixtures.py @@ -7,7 +7,6 @@ from buildcompiler.domain import IndexedBackbone, IndexedReagent, StageStatus from buildcompiler.inventory import Inventory - COLLECTION_PATHS = [ "tests/test_files/CIDARMoCloParts_collection.xml", "tests/test_files/CIDARMoCloPlasmidsKit_collection.xml", diff --git a/tests/integration/test_missing_lvl1_then_domestication.py b/tests/integration/test_missing_lvl1_then_domestication.py index a0a91a6..036c96d 100644 --- a/tests/integration/test_missing_lvl1_then_domestication.py +++ b/tests/integration/test_missing_lvl1_then_domestication.py @@ -12,8 +12,8 @@ StageStatus, ) from buildcompiler.execution import BuildContext, FullBuildExecutor -from buildcompiler.planning import BuildPlan from buildcompiler.inventory import Inventory +from buildcompiler.planning import BuildPlan from buildcompiler.sbol import SbolResolver diff --git a/tests/integration/test_offline_quickstart_notebook.py b/tests/integration/test_offline_quickstart_notebook.py index d6e93a4..7ba342b 100644 --- a/tests/integration/test_offline_quickstart_notebook.py +++ b/tests/integration/test_offline_quickstart_notebook.py @@ -2,7 +2,6 @@ import pytest - nbformat = pytest.importorskip("nbformat") nbclient = pytest.importorskip("nbclient") diff --git a/tests/unit/api/test_synbiohub_factory.py b/tests/unit/api/test_synbiohub_factory.py index a1d9e1b..0ab646b 100644 --- a/tests/unit/api/test_synbiohub_factory.py +++ b/tests/unit/api/test_synbiohub_factory.py @@ -1,5 +1,6 @@ import subprocess import sys +from typing import ClassVar import pytest import sbol2 @@ -16,9 +17,9 @@ class FakePartShop: - resources = {} - calls = [] - instances = [] + resources: ClassVar[dict] = {} + calls: ClassVar[list] = [] + instances: ClassVar[list] = [] failure = None def __init__(self, registry): diff --git a/tests/unit/planning/test_combinatorial.py b/tests/unit/planning/test_combinatorial.py index ea54215..fbacbb6 100644 --- a/tests/unit/planning/test_combinatorial.py +++ b/tests/unit/planning/test_combinatorial.py @@ -1,4 +1,5 @@ import sbol2 + from buildcompiler.api import BuildOptions from buildcompiler.planning.combinatorial import expand_combinatorial_derivation @@ -29,7 +30,7 @@ def _build_comb(valid=True): def test_expansion_and_blocking_behaviors(): comb = _build_comb(valid=True) - reqs, unsupported, warnings = expand_combinatorial_derivation( + reqs, unsupported, _warnings = expand_combinatorial_derivation( comb, options=BuildOptions() ) assert len(reqs) == 1 and unsupported == [] and reqs[0].variant_index == 0 @@ -77,7 +78,7 @@ def test_part_order_follows_template_sequence_not_variable_ids(): doc.add(part) expected_order.append(part.identity) vc.variants = [part.identity] - reqs, unsupported, warnings = expand_combinatorial_derivation( + reqs, unsupported, _warnings = expand_combinatorial_derivation( comb, options=BuildOptions() ) assert len(reqs) == 1 and unsupported == [] diff --git a/tests/unit/planning/test_domestication_planner.py b/tests/unit/planning/test_domestication_planner.py index 2767b0c..dfd9e03 100644 --- a/tests/unit/planning/test_domestication_planner.py +++ b/tests/unit/planning/test_domestication_planner.py @@ -1,5 +1,5 @@ -import sbol2 import pytest +import sbol2 from buildcompiler.constants import PART_ROLES from buildcompiler.planning import ( @@ -26,7 +26,7 @@ def _part( def test_supported_role_produces_plan() -> None: planner = DomesticationPlanner() - part = _part("https://example.org/p", sorted(PART_ROLES)[0], "ATGCGT") + part = _part("https://example.org/p", min(PART_ROLES), "ATGCGT") plan = planner.plan(part) assert plan.part_identity == part.identity assert plan.part_role in {"promoter", "rbs", "cds", "terminator"} @@ -41,7 +41,7 @@ def test_unsupported_role_fails_structurally() -> None: def test_missing_sequence_fails() -> None: planner = DomesticationPlanner() - part = _part("https://example.org/p2", sorted(PART_ROLES)[0]) + part = _part("https://example.org/p2", min(PART_ROLES)) with pytest.raises(ValueError, match="missing a usable DNA sequence"): planner.plan(part) @@ -49,7 +49,7 @@ def test_missing_sequence_fails() -> None: def test_bsai_sites_create_edit_proposals_without_mutating_sequence() -> None: planner = DomesticationPlanner() original = "AAAGGTCTCTTT" - part = _part("https://example.org/p3", sorted(PART_ROLES)[0], original) + part = _part("https://example.org/p3", min(PART_ROLES), original) plan = planner.plan(part) assert len(plan.sequence_edit_proposals) == 1 assert plan.sequence_edit_proposals[0].site_sequence == "GGTCTC" diff --git a/tests/unit/planning/test_validation.py b/tests/unit/planning/test_validation.py index 32b53ef..ba98197 100644 --- a/tests/unit/planning/test_validation.py +++ b/tests/unit/planning/test_validation.py @@ -1,4 +1,5 @@ import sbol2 + from buildcompiler.planning.validation import ( ordered_lvl1_parts, validate_lvl1_cardinality, @@ -35,7 +36,7 @@ def test_validate_cardinality_and_order_fallback_and_warning(): ok, warnings = validate_lvl1_cardinality(lvl1) assert ok is True assert warnings == [] - ordered, ow = ordered_lvl1_parts(lvl1) + ordered, _warnings = ordered_lvl1_parts(lvl1) assert len(ordered) == 4 diff --git a/tests/unit/sbol/test_assembly_service.py b/tests/unit/sbol/test_assembly_service.py index 4a977a4..70afc7d 100644 --- a/tests/unit/sbol/test_assembly_service.py +++ b/tests/unit/sbol/test_assembly_service.py @@ -1,5 +1,5 @@ -import sbol2 import pytest +import sbol2 from buildcompiler.domain import ( BuildStage, diff --git a/tests/unit/sbol/test_resolver.py b/tests/unit/sbol/test_resolver.py index 8db078b..dfc8787 100644 --- a/tests/unit/sbol/test_resolver.py +++ b/tests/unit/sbol/test_resolver.py @@ -1,5 +1,5 @@ -import sbol2 import pytest +import sbol2 from buildcompiler.sbol import PullPolicy, SbolResolver diff --git a/tests/unit/stages/test_assembly_lvl2.py b/tests/unit/stages/test_assembly_lvl2.py index 84d8580..30b34d1 100644 --- a/tests/unit/stages/test_assembly_lvl2.py +++ b/tests/unit/stages/test_assembly_lvl2.py @@ -210,8 +210,8 @@ def test_assembly_lvl2_incomplete_region_order_blocks_as_hard_constraint(): def test_buildcompiler_assembly_lvl2_respects_supplied_backbone(monkeypatch): from types import SimpleNamespace - from buildcompiler.buildcompiler import BuildCompiler import buildcompiler.buildcompiler as buildcompiler_module + from buildcompiler.buildcompiler import BuildCompiler compiler = BuildCompiler.from_local_documents([]) compiler.BbsI_impl = SimpleNamespace(built="https://example.org/reagents/BbsI") diff --git a/tests/unit/test_core_imports.py b/tests/unit/test_core_imports.py index bc45619..9231d69 100644 --- a/tests/unit/test_core_imports.py +++ b/tests/unit/test_core_imports.py @@ -3,20 +3,22 @@ def test_core_imports_do_not_load_optional_automation_dependencies(): import buildcompiler - from buildcompiler.adapters.pudu import ( - assembly_route_to_pudu_json, - plating_to_pudu_json, - transformation_to_pudu_json, - ) - from buildcompiler.api import BuildCompiler, BuildOptions from buildcompiler import ( BuildCompiler as RootBuildCompiler, + ) + from buildcompiler import ( assembly_lvl1, assembly_lvl2, domestication, full_build, transformation, ) + from buildcompiler.adapters.pudu import ( + assembly_route_to_pudu_json, + plating_to_pudu_json, + transformation_to_pudu_json, + ) + from buildcompiler.api import BuildCompiler, BuildOptions from buildcompiler.execution import FullBuildExecutor from buildcompiler.reporting import BuildGraph, BuildReport, BuildSummary diff --git a/uv.lock b/uv.lock index c240036..14b4a94 100644 --- a/uv.lock +++ b/uv.lock @@ -2563,28 +2563,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/b9/9bd84453ed6dd04688de9b3f3a4146a1698e8faae2ceeccce4e14c67ae17/ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57", size = 5452071, upload-time = "2025-10-07T18:21:55.763Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/4e/79d463a5f80654e93fa653ebfb98e0becc3f0e7cf6219c9ddedf1e197072/ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3", size = 12494532, upload-time = "2025-10-07T18:21:00.373Z" }, - { url = "https://files.pythonhosted.org/packages/ee/40/e2392f445ed8e02aa6105d49db4bfff01957379064c30f4811c3bf38aece/ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8", size = 13160768, upload-time = "2025-10-07T18:21:04.73Z" }, - { url = "https://files.pythonhosted.org/packages/75/da/2a656ea7c6b9bd14c7209918268dd40e1e6cea65f4bb9880eaaa43b055cd/ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8", size = 12363376, upload-time = "2025-10-07T18:21:07.833Z" }, - { url = "https://files.pythonhosted.org/packages/42/e2/1ffef5a1875add82416ff388fcb7ea8b22a53be67a638487937aea81af27/ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7", size = 12608055, upload-time = "2025-10-07T18:21:10.72Z" }, - { url = "https://files.pythonhosted.org/packages/4a/32/986725199d7cee510d9f1dfdf95bf1efc5fa9dd714d0d85c1fb1f6be3bc3/ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7", size = 12318544, upload-time = "2025-10-07T18:21:13.741Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ed/4969cefd53315164c94eaf4da7cfba1f267dc275b0abdd593d11c90829a3/ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2", size = 14001280, upload-time = "2025-10-07T18:21:16.411Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ad/96c1fc9f8854c37681c9613d825925c7f24ca1acfc62a4eb3896b50bacd2/ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c", size = 15027286, upload-time = "2025-10-07T18:21:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/b3/00/1426978f97df4fe331074baf69615f579dc4e7c37bb4c6f57c2aad80c87f/ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e", size = 14451506, upload-time = "2025-10-07T18:21:22.779Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/9c1cea6e493c0cf0647674cca26b579ea9d2a213b74b5c195fbeb9678e15/ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206", size = 13437384, upload-time = "2025-10-07T18:21:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/29/b4/4cd6a4331e999fc05d9d77729c95503f99eae3ba1160469f2b64866964e3/ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e", size = 13447976, upload-time = "2025-10-07T18:21:28.83Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c0/ac42f546d07e4f49f62332576cb845d45c67cf5610d1851254e341d563b6/ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd", size = 13682850, upload-time = "2025-10-07T18:21:31.842Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c4/4b0c9bcadd45b4c29fe1af9c5d1dc0ca87b4021665dfbe1c4688d407aa20/ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d", size = 12449825, upload-time = "2025-10-07T18:21:35.074Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a8/e2e76288e6c16540fa820d148d83e55f15e994d852485f221b9524514730/ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f", size = 12272599, upload-time = "2025-10-07T18:21:38.08Z" }, - { url = "https://files.pythonhosted.org/packages/18/14/e2815d8eff847391af632b22422b8207704222ff575dec8d044f9ab779b2/ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02", size = 13193828, upload-time = "2025-10-07T18:21:41.216Z" }, - { url = "https://files.pythonhosted.org/packages/44/c6/61ccc2987cf0aecc588ff8f3212dea64840770e60d78f5606cd7dc34de32/ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296", size = 13628617, upload-time = "2025-10-07T18:21:44.04Z" }, - { url = "https://files.pythonhosted.org/packages/73/e6/03b882225a1b0627e75339b420883dc3c90707a8917d2284abef7a58d317/ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543", size = 12367872, upload-time = "2025-10-07T18:21:46.67Z" }, - { url = "https://files.pythonhosted.org/packages/41/77/56cf9cf01ea0bfcc662de72540812e5ba8e9563f33ef3d37ab2174892c47/ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2", size = 13464628, upload-time = "2025-10-07T18:21:50.318Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2a/65880dfd0e13f7f13a775998f34703674a4554906167dce02daf7865b954/ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730", size = 12565142, upload-time = "2025-10-07T18:21:53.577Z" }, +version = "0.16.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/78/449cb84790bd5cc3823b2652ee405a4558856e5c4195aee3a16bf7b3eb5d/ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b", size = 4938814, upload-time = "2026-09-16T15:54:46.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/25/6071aabc530e9be7e2c195e8fe3f7aea2735405b6cf447212832d7811831/ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38", size = 10048966, upload-time = "2026-09-16T15:53:57.605Z" }, + { url = "https://files.pythonhosted.org/packages/54/98/07f90ecbc74dd5fb5764f11f2bc774d6a7cffef92d2ff5f5b4e9e23c754e/ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e", size = 10165498, upload-time = "2026-09-16T15:54:01.14Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1f/e6a712e3b47cad4a40600134105ed193cb773f618a42eb7ba323cb812cc0/ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170", size = 9830004, upload-time = "2026-09-16T15:54:03.998Z" }, + { url = "https://files.pythonhosted.org/packages/23/f2/311a08776d75d81c7676e20b6b020ae63cbe881fcdc7a8dd64e6e18bdd93/ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659", size = 9986558, upload-time = "2026-09-16T15:54:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ed/37b6cb3d3ba8c73e68ae3eb1d502383beb5aa05a582bb7bb3a922f929f54/ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2", size = 9877332, upload-time = "2026-09-16T15:54:09.552Z" }, + { url = "https://files.pythonhosted.org/packages/22/cc/40873a8f36ad084cc540d55fcca7077264d5b13b24659e9180c176fb2b08/ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812", size = 10507125, upload-time = "2026-09-16T15:54:12.152Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e4/fc91a642b78ccbab6b9477720f3644ae7a10a9bcce69a934679cd64f62bc/ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b", size = 11336694, upload-time = "2026-09-16T15:54:15.489Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/bbd2a9a600a4e73dc3e7548a249c8d1671273464b55822c6fae50f602dff/ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f", size = 10774448, upload-time = "2026-09-16T15:54:18.16Z" }, + { url = "https://files.pythonhosted.org/packages/1a/41/d83af9879a7b6e8bf5fe16b1da0b134049d2f5d3afac12defb0897cb84bd/ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df", size = 10323796, upload-time = "2026-09-16T15:54:20.743Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/cefd07bfe914b84943ea769ade8d607bd22750b965d3228eefd7cebd15d0/ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e", size = 10514115, upload-time = "2026-09-16T15:54:23.497Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9d/76a2e26c79a23be6e6e3664c57bec9e9fc8de155cfb9e4b67ea91b64f9d7/ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f", size = 10072582, upload-time = "2026-09-16T15:54:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d4/f42edddb39668af1a559ceafa3823aedd65633a48dc9768e775485faa2c1/ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2", size = 9879644, upload-time = "2026-09-16T15:54:29.278Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/913e3195d95e0378786c6656945c865f534a3560e29139da4882aff630d1/ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9", size = 10231569, upload-time = "2026-09-16T15:54:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c4/8aa6ea0bdcedbd1bf87397e2fc4ed8406448ea5842f8660bc6e5f163039d/ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773", size = 10663666, upload-time = "2026-09-16T15:54:34.838Z" }, + { url = "https://files.pythonhosted.org/packages/3d/02/7f10ef4700bc223c30a3fdd10631a29830c45524b810a3c7ed947af64591/ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f", size = 10093472, upload-time = "2026-09-16T15:54:37.47Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5d/a509c07d714b6da88f2c518b4637cf6f1d46b074be8f0f1e5fb9ff5126fe/ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa", size = 10586899, upload-time = "2026-09-16T15:54:40.488Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a0/50787329e4f20bf9dc9f6230015d46ec69c51a97ace5bc202dae4755365d/ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a", size = 10386316, upload-time = "2026-09-16T15:54:43.332Z" }, ] [[package]] @@ -2740,7 +2739,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'", specifier = ">=7,<9" }, { name = "pytest-cov", extras = ["all"], marker = "extra == 'dev'" }, { name = "pytest-cov", extras = ["all"], marker = "extra == 'test'" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = "==0.16.8" }, { name = "sbol2" }, { name = "sbolinventory", marker = "python_full_version >= '3.10' and extra == 'automation'", git = "https://github.com/DRAGGON-Lab/SBOLInventory.git" }, { name = "twine", marker = "extra == 'dev'", specifier = ">=5.0" }, @@ -2748,7 +2747,7 @@ requires-dist = [ provides-extras = ["test", "dev", "automation", "notebook"] [package.metadata.requires-dev] -dev = [{ name = "ruff", specifier = ">=0.14.0" }] +dev = [{ name = "ruff", specifier = "==0.16.8" }] [[package]] name = "terminado"