From 61b850bf46f2e7164f711974289415e0956583bc Mon Sep 17 00:00:00 2001 From: smillmorel Date: Wed, 16 Sep 2026 13:20:36 -0400 Subject: [PATCH] style: resolve the remaining ruff findings Auto-fixes (UP037, UP038, F401, I001, SIM105, RUF100) plus hand-fixes: drop the now-unused `Union` imports, annotate mutable class attributes with ClassVar, raise ValidationError instead of blind Exception in the zero-length test, delete two dead locals, and bind the per-iteration grid-builder closures to their loop variables (B023). --- src/otko/commands/__init__.py | 16 ++-- src/otko/commands/analysis.py | 8 +- src/otko/commands/base.py | 6 +- src/otko/commands/combinations.py | 8 +- src/otko/commands/constraints.py | 6 +- src/otko/commands/elements.py | 14 ++-- src/otko/commands/grid.py | 4 +- src/otko/commands/loads.py | 4 +- src/otko/commands/materials.py | 6 +- src/otko/commands/nodes.py | 12 ++- src/otko/commands/project.py | 2 +- src/otko/commands/sections.py | 6 +- src/otko/commands/series_and_patterns.py | 12 +-- src/otko/commands/transforms.py | 9 +- src/otko/core/analysis/__init__.py | 4 +- src/otko/core/geometry/elements.py | 22 +++-- src/otko/core/geometry/grid.py | 2 +- src/otko/core/loads/__init__.py | 8 +- src/otko/core/materials/__init__.py | 26 +++--- src/otko/core/project.py | 8 +- src/otko/core/sections/__init__.py | 10 +-- src/otko/services/animation_export.py | 3 +- src/otko/services/export.py | 2 +- src/otko/services/peer_record.py | 10 +-- src/otko/services/section_bbox.py | 2 +- src/otko/services/section_properties.py | 1 - src/otko/viewmodels/project_vm.py | 5 +- src/otko/views/action_handlers.py | 2 +- src/otko/views/canvas3d/diagram_renderer.py | 5 +- src/otko/views/canvas3d/model_renderer.py | 84 ++++++++++--------- src/otko/views/dialogs/assign_load.py | 1 - src/otko/views/dialogs/assign_masses.py | 6 +- src/otko/views/dialogs/assign_support.py | 2 - src/otko/views/dialogs/case_forms.py | 1 - src/otko/views/dialogs/case_manager.py | 4 +- src/otko/views/dialogs/define_grid_data.py | 13 ++- src/otko/views/dialogs/grid_system.py | 2 +- src/otko/views/dialogs/locate_origin.py | 1 - src/otko/views/dialogs/material_library.py | 3 +- src/otko/views/dialogs/path_time_series.py | 4 +- src/otko/views/dialogs/quick_grid_lines.py | 1 - src/otko/views/dialogs/run_analysis.py | 4 +- src/otko/views/dialogs/section_editor.py | 2 - src/otko/views/dialogs/section_library.py | 2 +- src/otko/views/dialogs/uniform_excitation.py | 1 - src/otko/views/docks/deformed_shape.py | 2 +- src/otko/views/docks/force_diagram.py | 2 - src/otko/views/docks/hysteresis.py | 5 +- src/otko/views/docks/property_editor.py | 4 +- src/otko/views/docks/pushover_curve.py | 2 +- src/otko/views/docks/response_spectrum.py | 3 +- src/otko/views/docks/table_dock.py | 4 +- src/otko/views/docks/time_history.py | 6 +- src/otko/views/main_window.py | 2 +- src/otko/views/tools/base.py | 6 +- src/otko/views/tools/draw_frame.py | 6 +- src/otko/views/tools/draw_node.py | 6 +- src/otko/views/tools/draw_truss.py | 6 +- tests/gui/test_analysis_commands.py | 6 +- tests/gui/test_analysis_runner.py | 8 +- tests/gui/test_assign_load_pattern_name.py | 9 +- tests/gui/test_assign_masses.py | 2 +- tests/gui/test_assign_zls.py | 4 +- tests/gui/test_canvas_grid_snap.py | 2 +- tests/gui/test_case_forms.py | 4 +- tests/gui/test_commands.py | 7 +- tests/gui/test_convert_element_type.py | 6 +- tests/gui/test_define_dialogs.py | 6 +- tests/gui/test_draw_frame_tool.py | 10 +-- tests/gui/test_draw_node_tool.py | 8 +- tests/gui/test_draw_truss_tool.py | 9 +- tests/gui/test_grid_dialog.py | 2 +- tests/gui/test_material_dialog_concrete04.py | 6 +- tests/gui/test_material_form_ranges.py | 2 +- tests/gui/test_path_ts_dialogs.py | 7 +- tests/gui/test_property_commands.py | 6 +- tests/gui/test_property_forms.py | 9 +- tests/gui/test_pushover_curve_units.py | 6 +- tests/gui/test_run_analysis.py | 8 +- tests/gui/test_section_extrusion.py | 2 +- tests/gui/test_section_forms.py | 4 +- tests/gui/test_selection.py | 2 +- tests/gui/test_transforms.py | 6 +- tests/gui/test_tree_multi_select.py | 5 +- tests/gui/test_units_status_bar.py | 2 +- tests/gui/test_update_element_fields.py | 6 +- tests/gui/test_working_plane.py | 2 +- tests/integration/test_basic_truss.py | 5 +- tests/integration/test_beam_quad_2d.py | 4 +- tests/integration/test_combinations.py | 6 +- tests/integration/test_concrete04_runner.py | 4 +- tests/integration/test_dof_coverage.py | 4 +- .../test_eigen_two_storey_one_bay_frame.py | 6 +- .../test_eigen_two_storey_shear_frame.py | 6 +- tests/integration/test_elastic_frame.py | 12 ++- tests/integration/test_ex1a_canti2d.py | 6 +- tests/integration/test_ex1a_canti2d_eq.py | 4 +- tests/integration/test_ex1b_portal2d.py | 6 +- .../test_ex2a_canti2d_elastic_element.py | 6 +- .../test_ex2b_canti2d_inelastic_section.py | 6 +- ...st_ex2c_canti2d_inelastic_fiber_section.py | 6 +- .../integration/test_ex3_canti2d_variants.py | 6 +- .../integration/test_ex4_portal2d_variants.py | 6 +- tests/integration/test_moment_curvature.py | 8 +- .../test_moment_curvature_example.py | 6 +- tests/integration/test_pattern_factors.py | 4 +- tests/integration/test_rc_frame_earthquake.py | 7 +- tests/integration/test_rc_frame_gravity.py | 6 +- tests/integration/test_rc_frame_pushover.py | 7 +- .../integration/test_runner_imposed_motion.py | 4 +- tests/integration/test_runner_modal.py | 6 +- tests/integration/test_runner_static.py | 4 +- tests/integration/test_runner_transient.py | 9 +- tests/services/test_element_forces.py | 1 - tests/services/test_runner_translation.py | 1 - tests/services/test_section_bbox.py | 1 - tests/tools/test_parse_schemas.py | 1 + tests/unit/core/materials/test_concrete04.py | 1 + tests/unit/test_diagram_renderer.py | 10 +-- tests/unit/test_elements.py | 2 +- tests/unit/test_grid_system.py | 3 +- tests/unit/test_phase8a.py | 1 - tests/unit/test_phase9.py | 4 +- tests/unit/test_renderer.py | 8 +- tests/unit/test_unit_labels.py | 2 - tests/unit/test_zerolength_section.py | 3 +- 126 files changed, 359 insertions(+), 397 deletions(-) diff --git a/src/otko/commands/__init__.py b/src/otko/commands/__init__.py index 82ccef5..eddf86f 100644 --- a/src/otko/commands/__init__.py +++ b/src/otko/commands/__init__.py @@ -29,14 +29,6 @@ from otko.commands.grid import ( SetCoordSystemsCommand, SetGridSystemCommand, ) -from otko.commands.series_and_patterns import ( - AddLoadPatternCommand, - AddTimeSeriesCommand, - DeleteLoadPatternCommand, - DeleteTimeSeriesCommand, - UpdateLoadPatternCommand, - UpdateTimeSeriesCommand, -) from otko.commands.loads import ( AddElementLoadsCommand, AddNodalLoadsCommand, @@ -63,6 +55,14 @@ from otko.commands.sections import ( DeleteSectionsCommand, UpdateSectionCommand, ) +from otko.commands.series_and_patterns import ( + AddLoadPatternCommand, + AddTimeSeriesCommand, + DeleteLoadPatternCommand, + DeleteTimeSeriesCommand, + UpdateLoadPatternCommand, + UpdateTimeSeriesCommand, +) from otko.commands.transforms import ( MirrorCommand, MoveNodesCommand, diff --git a/src/otko/commands/analysis.py b/src/otko/commands/analysis.py index 5950b40..444f34c 100644 --- a/src/otko/commands/analysis.py +++ b/src/otko/commands/analysis.py @@ -13,9 +13,7 @@ if TYPE_CHECKING: class AddAnalysisCasesCommand(ProjectCommand): """Add one or more analysis cases.""" - def __init__( - self, vm: "ProjectViewModel", cases: list[Any], *, text: str | None = None - ) -> None: + def __init__(self, vm: ProjectViewModel, cases: list[Any], *, text: str | None = None) -> None: super().__init__(vm, text or f"Add {len(cases)} analysis case(s)") self._cases = list(cases) @@ -36,7 +34,7 @@ class AddAnalysisCasesCommand(ProjectCommand): class DeleteAnalysisCasesCommand(ProjectCommand): """Remove a set of analysis cases.""" - def __init__(self, vm: "ProjectViewModel", case_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, case_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(case_ids)} analysis case(s)") self._case_ids = set(case_ids) self._removed: list[tuple[int, Any]] = [] @@ -58,7 +56,7 @@ class DeleteAnalysisCasesCommand(ProjectCommand): class UpdateAnalysisCaseCommand(ProjectCommand): """Replace an analysis case at a given id.""" - def __init__(self, vm: "ProjectViewModel", new_case: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_case: Any) -> None: super().__init__(vm, f"Edit analysis case {new_case.id}") self._new = new_case self._old: Any | None = None diff --git a/src/otko/commands/base.py b/src/otko/commands/base.py index 15f616f..0e8b3dd 100644 --- a/src/otko/commands/base.py +++ b/src/otko/commands/base.py @@ -27,16 +27,16 @@ class ProjectCommand(QUndoCommand): :meth:`_notify` exactly once at the end of each. """ - def __init__(self, vm: "ProjectViewModel", text: str) -> None: + def __init__(self, vm: ProjectViewModel, text: str) -> None: super().__init__(text) self._vm = vm @property - def vm(self) -> "ProjectViewModel": + def vm(self) -> ProjectViewModel: return self._vm @property - def project(self) -> "Project": + def project(self) -> Project: if self._vm.project is None: raise RuntimeError(f"Cannot apply '{self.text()}': no active project.") return self._vm.project diff --git a/src/otko/commands/combinations.py b/src/otko/commands/combinations.py index 0af9a08..23f1aa8 100644 --- a/src/otko/commands/combinations.py +++ b/src/otko/commands/combinations.py @@ -13,9 +13,7 @@ if TYPE_CHECKING: class AddCombinationsCommand(ProjectCommand): """Add one or more load combinations.""" - def __init__( - self, vm: "ProjectViewModel", combos: list[Any], *, text: str | None = None - ) -> None: + def __init__(self, vm: ProjectViewModel, combos: list[Any], *, text: str | None = None) -> None: super().__init__(vm, text or f"Add {len(combos)} load combination(s)") self._combos = list(combos) @@ -36,7 +34,7 @@ class AddCombinationsCommand(ProjectCommand): class DeleteCombinationsCommand(ProjectCommand): """Remove a set of load combinations.""" - def __init__(self, vm: "ProjectViewModel", combo_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, combo_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(combo_ids)} load combination(s)") self._combo_ids = set(combo_ids) self._removed: list[tuple[int, Any]] = [] @@ -60,7 +58,7 @@ class DeleteCombinationsCommand(ProjectCommand): class UpdateCombinationCommand(ProjectCommand): """Replace a load combination at a given id.""" - def __init__(self, vm: "ProjectViewModel", new_combo: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_combo: Any) -> None: super().__init__(vm, f"Edit load combination {new_combo.id}") self._new = new_combo self._old: Any | None = None diff --git a/src/otko/commands/constraints.py b/src/otko/commands/constraints.py index e3df65a..3f94205 100644 --- a/src/otko/commands/constraints.py +++ b/src/otko/commands/constraints.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class AddEqualDOFConstraintCommand(ProjectCommand): """Append an ``equalDOF`` constraint to the project (undoable).""" - def __init__(self, vm: "ProjectViewModel", constraint: EqualDOFConstraint) -> None: + def __init__(self, vm: ProjectViewModel, constraint: EqualDOFConstraint) -> None: super().__init__( vm, f"Add equalDOF {constraint.retained_node}->{constraint.constrained_node}", @@ -48,7 +48,7 @@ class UpdateEqualDOFConstraintCommand(ProjectCommand): """ def __init__( - self, vm: "ProjectViewModel", index: int, new_constraint: EqualDOFConstraint + self, vm: ProjectViewModel, index: int, new_constraint: EqualDOFConstraint ) -> None: super().__init__(vm, f"Edit equalDOF row {index + 1}") self._index = index @@ -71,7 +71,7 @@ class UpdateEqualDOFConstraintCommand(ProjectCommand): class DeleteEqualDOFConstraintCommand(ProjectCommand): """Remove ``equalDOF`` constraints by table-row index (no cascade).""" - def __init__(self, vm: "ProjectViewModel", indices: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, indices: set[int]) -> None: super().__init__(vm, f"Delete {len(indices)} constraint(s)") self._indices = set(indices) self._removed: list[tuple[int, EqualDOFConstraint]] = [] diff --git a/src/otko/commands/elements.py b/src/otko/commands/elements.py index 6487a9d..f1967ba 100644 --- a/src/otko/commands/elements.py +++ b/src/otko/commands/elements.py @@ -14,7 +14,7 @@ class AddElementsCommand(ProjectCommand): """Add one or more elements in a single undoable step.""" def __init__( - self, vm: "ProjectViewModel", elements: list[Any], *, text: str | None = None + self, vm: ProjectViewModel, elements: list[Any], *, text: str | None = None ) -> None: super().__init__(vm, text or f"Add {len(elements)} element(s)") self._elements = list(elements) @@ -40,7 +40,7 @@ class AddElementsCommand(ProjectCommand): class DeleteElementsCommand(ProjectCommand): """Remove a set of elements (no cascade — nodes are not affected).""" - def __init__(self, vm: "ProjectViewModel", element_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, element_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(element_ids)} element(s)") self._element_ids = set(element_ids) self._removed: list[tuple[int, Any]] = [] @@ -68,7 +68,7 @@ class AssignSectionCommand(ProjectCommand): (e.g. trusses, zero-length elements). """ - def __init__(self, vm: "ProjectViewModel", element_ids: set[int], section_id: int) -> None: + def __init__(self, vm: ProjectViewModel, element_ids: set[int], section_id: int) -> None: super().__init__(vm, f"Assign section {section_id} to {len(element_ids)} element(s)") self._element_ids = set(element_ids) self._section_id = section_id @@ -106,7 +106,7 @@ class ConvertElementTypeCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, element_ids: set[int], target_type: str, defaults: dict[str, Any] | None = None, @@ -185,7 +185,7 @@ class ReplaceElementsCommand(ProjectCommand): for another while preserving the id and node connectivity. """ - def __init__(self, vm: "ProjectViewModel", replacements: list[Any]) -> None: + def __init__(self, vm: ProjectViewModel, replacements: list[Any]) -> None: super().__init__(vm, f"Replace {len(replacements)} element(s)") self._replacements = {el.id: el for el in replacements} self._previous: dict[int, Any] = {} @@ -217,7 +217,7 @@ class UpdateElementFieldsCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, element_id: int, fields: dict[str, Any], ) -> None: @@ -254,7 +254,7 @@ class UpdateElementFieldsCommand(ProjectCommand): class AssignMaterialCommand(ProjectCommand): """Set ``material_id`` on a set of elements (truss-style).""" - def __init__(self, vm: "ProjectViewModel", element_ids: set[int], material_id: int) -> None: + def __init__(self, vm: ProjectViewModel, element_ids: set[int], material_id: int) -> None: super().__init__(vm, f"Assign material {material_id} to {len(element_ids)} element(s)") self._element_ids = set(element_ids) self._material_id = material_id diff --git a/src/otko/commands/grid.py b/src/otko/commands/grid.py index 894c56b..e271746 100644 --- a/src/otko/commands/grid.py +++ b/src/otko/commands/grid.py @@ -20,7 +20,7 @@ class SetCoordSystemsCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, new_systems: list[CoordinateGridSystem], ) -> None: super().__init__(vm, "Update coordinate/grid systems") @@ -46,7 +46,7 @@ class SetGridSystemCommand(ProjectCommand): systems at once. """ - def __init__(self, vm: "ProjectViewModel", new_grid: GridSystem) -> None: + def __init__(self, vm: ProjectViewModel, new_grid: GridSystem) -> None: super().__init__(vm, "Update grid system") self._new_grid = new_grid self._previous: GridSystem | None = None diff --git a/src/otko/commands/loads.py b/src/otko/commands/loads.py index d24fabf..789b6c8 100644 --- a/src/otko/commands/loads.py +++ b/src/otko/commands/loads.py @@ -35,7 +35,7 @@ class AddNodalLoadsCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], forces: tuple[float, float, float, float, float, float], pattern_id: int | None = None, @@ -122,7 +122,7 @@ class AddElementLoadsCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, element_ids: set[int], wy: float = 0.0, wz: float = 0.0, diff --git a/src/otko/commands/materials.py b/src/otko/commands/materials.py index 836ade9..9729ced 100644 --- a/src/otko/commands/materials.py +++ b/src/otko/commands/materials.py @@ -14,7 +14,7 @@ class AddMaterialsCommand(ProjectCommand): """Add one or more materials in a single undoable step.""" def __init__( - self, vm: "ProjectViewModel", materials: list[Any], *, text: str | None = None + self, vm: ProjectViewModel, materials: list[Any], *, text: str | None = None ) -> None: super().__init__(vm, text or f"Add {len(materials)} material(s)") self._materials = list(materials) @@ -36,7 +36,7 @@ class AddMaterialsCommand(ProjectCommand): class DeleteMaterialsCommand(ProjectCommand): """Remove materials (no cascade).""" - def __init__(self, vm: "ProjectViewModel", material_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, material_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(material_ids)} material(s)") self._material_ids = set(material_ids) self._removed: list[tuple[int, Any]] = [] @@ -64,7 +64,7 @@ class UpdateMaterialCommand(ProjectCommand): otherwise this is an Add+Delete, not an update. """ - def __init__(self, vm: "ProjectViewModel", new_material: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_material: Any) -> None: super().__init__(vm, f"Edit material {new_material.id}") self._new = new_material self._old: Any | None = None diff --git a/src/otko/commands/nodes.py b/src/otko/commands/nodes.py index b737265..e24840c 100644 --- a/src/otko/commands/nodes.py +++ b/src/otko/commands/nodes.py @@ -14,9 +14,7 @@ if TYPE_CHECKING: class AddNodesCommand(ProjectCommand): """Add one or more nodes in a single undoable step.""" - def __init__( - self, vm: "ProjectViewModel", nodes: list[Node], *, text: str | None = None - ) -> None: + def __init__(self, vm: ProjectViewModel, nodes: list[Node], *, text: str | None = None) -> None: super().__init__(vm, text or f"Add {len(nodes)} node(s)") self._nodes = list(nodes) @@ -41,7 +39,7 @@ class DeleteNodesCommand(ProjectCommand): them in their original positions. """ - def __init__(self, vm: "ProjectViewModel", node_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, node_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(node_ids)} node(s)") self._node_ids = set(node_ids) self._removed_nodes: list[tuple[int, Node]] = [] # (index, node) @@ -95,7 +93,7 @@ class SetRestraintCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], restraint: tuple[bool, bool, bool, bool, bool, bool], ) -> None: @@ -130,7 +128,7 @@ class UpdateNodeFieldsCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_id: int, fields: dict[str, Any], ) -> None: @@ -168,7 +166,7 @@ class SetMassCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], mass: tuple[float, float, float, float, float, float], ) -> None: diff --git a/src/otko/commands/project.py b/src/otko/commands/project.py index 362e2a6..3fd0065 100644 --- a/src/otko/commands/project.py +++ b/src/otko/commands/project.py @@ -19,7 +19,7 @@ class SetUnitsCommand(ProjectCommand): so both redo and undo notify (dirty + modelMutated) to refresh. """ - def __init__(self, vm: "ProjectViewModel", units: UnitSystem) -> None: + def __init__(self, vm: ProjectViewModel, units: UnitSystem) -> None: super().__init__(vm, f"Set display units to {units.value}") self._units = units self._previous: UnitSystem | None = None diff --git a/src/otko/commands/sections.py b/src/otko/commands/sections.py index e75a54f..94142a6 100644 --- a/src/otko/commands/sections.py +++ b/src/otko/commands/sections.py @@ -14,7 +14,7 @@ class AddSectionsCommand(ProjectCommand): """Add one or more sections in a single undoable step.""" def __init__( - self, vm: "ProjectViewModel", sections: list[Any], *, text: str | None = None + self, vm: ProjectViewModel, sections: list[Any], *, text: str | None = None ) -> None: super().__init__(vm, text or f"Add {len(sections)} section(s)") self._sections = list(sections) @@ -36,7 +36,7 @@ class AddSectionsCommand(ProjectCommand): class DeleteSectionsCommand(ProjectCommand): """Remove sections (no cascade — elements referencing them must be cleaned separately).""" - def __init__(self, vm: "ProjectViewModel", section_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, section_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(section_ids)} section(s)") self._section_ids = set(section_ids) self._removed: list[tuple[int, Any]] = [] @@ -60,7 +60,7 @@ class DeleteSectionsCommand(ProjectCommand): class UpdateSectionCommand(ProjectCommand): """Replace a section's parameters at a given id.""" - def __init__(self, vm: "ProjectViewModel", new_section: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_section: Any) -> None: super().__init__(vm, f"Edit section {new_section.id}") self._new = new_section self._old: Any | None = None diff --git a/src/otko/commands/series_and_patterns.py b/src/otko/commands/series_and_patterns.py index 1d1062c..1d03ab5 100644 --- a/src/otko/commands/series_and_patterns.py +++ b/src/otko/commands/series_and_patterns.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: class AddTimeSeriesCommand(ProjectCommand): """Append a :class:`TimeSeries` to the project (undoable).""" - def __init__(self, vm: "ProjectViewModel", ts: "TimeSeries") -> None: + def __init__(self, vm: ProjectViewModel, ts: TimeSeries) -> None: super().__init__(vm, f"Add time series '{ts.name or ts.id}'") self._ts = ts @@ -41,7 +41,7 @@ class UpdateTimeSeriesCommand(ProjectCommand): The new series must carry the same id as the one being replaced. """ - def __init__(self, vm: "ProjectViewModel", new_ts: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_ts: Any) -> None: super().__init__(vm, f"Edit time series {new_ts.id}") self._new = new_ts self._old: Any | None = None @@ -67,7 +67,7 @@ class DeleteTimeSeriesCommand(ProjectCommand): """Remove time series (no cascade — patterns referencing them must be cleaned separately; ``validate_references`` catches strays before save).""" - def __init__(self, vm: "ProjectViewModel", ts_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, ts_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(ts_ids)} time serie(s)") self._ts_ids = set(ts_ids) self._removed: list[tuple[int, Any]] = [] @@ -91,7 +91,7 @@ class DeleteTimeSeriesCommand(ProjectCommand): class AddLoadPatternCommand(ProjectCommand): """Append a :class:`LoadPattern` to the project (undoable).""" - def __init__(self, vm: "ProjectViewModel", pattern: "LoadPattern") -> None: + def __init__(self, vm: ProjectViewModel, pattern: LoadPattern) -> None: super().__init__(vm, f"Add pattern '{pattern.name or pattern.id}'") self._pattern = pattern @@ -117,7 +117,7 @@ class UpdateLoadPatternCommand(ProjectCommand): drops the pattern's stored loads. """ - def __init__(self, vm: "ProjectViewModel", new_pattern: Any) -> None: + def __init__(self, vm: ProjectViewModel, new_pattern: Any) -> None: super().__init__(vm, f"Edit pattern {new_pattern.id}") self._new = new_pattern self._old: Any | None = None @@ -142,7 +142,7 @@ class UpdateLoadPatternCommand(ProjectCommand): class DeleteLoadPatternCommand(ProjectCommand): """Remove load patterns (their child nodal/element loads go with them).""" - def __init__(self, vm: "ProjectViewModel", pattern_ids: set[int]) -> None: + def __init__(self, vm: ProjectViewModel, pattern_ids: set[int]) -> None: super().__init__(vm, f"Delete {len(pattern_ids)} pattern(s)") self._pattern_ids = set(pattern_ids) self._removed: list[tuple[int, Any]] = [] diff --git a/src/otko/commands/transforms.py b/src/otko/commands/transforms.py index 3e48448..07c335d 100644 --- a/src/otko/commands/transforms.py +++ b/src/otko/commands/transforms.py @@ -15,10 +15,9 @@ All copies inherit the source node's restraint, mass, and name. from __future__ import annotations -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Literal from otko.commands.base import ProjectCommand -from otko.core import Node if TYPE_CHECKING: from otko.viewmodels import ProjectViewModel @@ -30,7 +29,7 @@ class MoveNodesCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], offset: tuple[float, float, float], ) -> None: @@ -63,7 +62,7 @@ class ReplicateCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], element_ids: set[int], offset: tuple[float, float, float], @@ -147,7 +146,7 @@ class MirrorCommand(ProjectCommand): def __init__( self, - vm: "ProjectViewModel", + vm: ProjectViewModel, node_ids: set[int], element_ids: set[int], plane: Plane, diff --git a/src/otko/core/analysis/__init__.py b/src/otko/core/analysis/__init__.py index b809bad..e8835b7 100644 --- a/src/otko/core/analysis/__init__.py +++ b/src/otko/core/analysis/__init__.py @@ -9,7 +9,7 @@ in Phase 6/8. from __future__ import annotations -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, PositiveFloat, PositiveInt, model_validator @@ -269,6 +269,6 @@ class ResponseSpectrumCase(Entity): AnalysisCase = Annotated[ - Union[StaticCase, ModalCase, TransientCase, PushoverCase, ResponseSpectrumCase], + StaticCase | ModalCase | TransientCase | PushoverCase | ResponseSpectrumCase, Field(discriminator="type"), ] diff --git a/src/otko/core/geometry/elements.py b/src/otko/core/geometry/elements.py index f79ae39..35a2026 100644 --- a/src/otko/core/geometry/elements.py +++ b/src/otko/core/geometry/elements.py @@ -10,7 +10,7 @@ Conventions follow OpenSeesPy ``element ...`` commands. from __future__ import annotations -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, PositiveFloat, PositiveInt @@ -198,16 +198,14 @@ class QuadElement(Entity): Element = Annotated[ - Union[ - TrussElement, - CorotTrussElement, - ElasticBeamColumn, - ForceBeamColumn, - DispBeamColumn, - ZeroLengthElement, - ZeroLengthSectionElement, - BeamWithHingesElement, - QuadElement, - ], + TrussElement + | CorotTrussElement + | ElasticBeamColumn + | ForceBeamColumn + | DispBeamColumn + | ZeroLengthElement + | ZeroLengthSectionElement + | BeamWithHingesElement + | QuadElement, Field(discriminator="type"), ] diff --git a/src/otko/core/geometry/grid.py b/src/otko/core/geometry/grid.py index cf6ecc7..f7d61ea 100644 --- a/src/otko/core/geometry/grid.py +++ b/src/otko/core/geometry/grid.py @@ -97,7 +97,7 @@ class GridSystem(BaseModel): return data @model_validator(mode="after") - def _sort_and_dedupe(self) -> "GridSystem": + def _sort_and_dedupe(self) -> GridSystem: for name in ("x_grid_lines", "y_grid_lines", "z_grid_lines"): lines: list[GridLine] = list(getattr(self, name)) lines.sort(key=lambda ln: ln.ordinate) diff --git a/src/otko/core/loads/__init__.py b/src/otko/core/loads/__init__.py index 283dfad..68a1557 100644 --- a/src/otko/core/loads/__init__.py +++ b/src/otko/core/loads/__init__.py @@ -12,7 +12,7 @@ pattern is deleted, its loads go with it. from __future__ import annotations -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import BaseModel, ConfigDict, Field, PositiveInt, model_validator @@ -110,7 +110,7 @@ class ResponseSpectrum(Entity): TimeSeries = Annotated[ - Union[LinearTimeSeries, ConstantTimeSeries, PathTimeSeries], + LinearTimeSeries | ConstantTimeSeries | PathTimeSeries, Field(discriminator="type"), ] @@ -229,7 +229,7 @@ class LoadCombination(Entity): ) @model_validator(mode="after") - def _check_items(self) -> "LoadCombination": + def _check_items(self) -> LoadCombination: """Reject duplicate case ids and non-finite factors.""" import math @@ -250,6 +250,6 @@ class LoadCombination(Entity): LoadPattern = Annotated[ - Union[PlainLoadPattern, UniformExcitationPattern, ImposedSupportMotionPattern], + PlainLoadPattern | UniformExcitationPattern | ImposedSupportMotionPattern, Field(discriminator="type"), ] diff --git a/src/otko/core/materials/__init__.py b/src/otko/core/materials/__init__.py index be71418..daa84f6 100644 --- a/src/otko/core/materials/__init__.py +++ b/src/otko/core/materials/__init__.py @@ -11,7 +11,7 @@ https://openseespydoc.readthedocs.io/en/latest/src/uniaxialMaterial.html from __future__ import annotations -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, PositiveFloat, model_validator @@ -130,7 +130,7 @@ class Concrete04(Entity): ) @model_validator(mode="after") - def _tensile_params_consistent(self) -> "Concrete04": + def _tensile_params_consistent(self) -> Concrete04: if self.fct is not None and self.et is None: raise ValueError("et is required when fct is given.") if self.et is not None and self.fct is None: @@ -229,18 +229,16 @@ class HystereticSM(Entity): # ──────────────────────────── Discriminated union ──────────────────────────── Material = Annotated[ - Union[ - ElasticIsotropic, - ElasticUniaxial, - Steel01, - Steel02, - Concrete01, - Concrete02, - Concrete04, - ElasticPP, - HystereticMaterial, - HystereticSM, - ], + ElasticIsotropic + | ElasticUniaxial + | Steel01 + | Steel02 + | Concrete01 + | Concrete02 + | Concrete04 + | ElasticPP + | HystereticMaterial + | HystereticSM, Field(discriminator="type"), ] """Tagged union of every material kind. Pydantic uses ``type`` to dispatch on JSON load.""" diff --git a/src/otko/core/project.py b/src/otko/core/project.py index 175f7a4..f2d030a 100644 --- a/src/otko/core/project.py +++ b/src/otko/core/project.py @@ -166,7 +166,7 @@ class Project(BaseModel): return data @model_validator(mode="after") - def _ensure_global_system(self) -> "Project": + def _ensure_global_system(self) -> Project: """Guarantee that a 'Global' entry exists as the first coord system.""" has_global = any(cs.name == "Global" for cs in self.coord_systems) if not has_global: @@ -174,7 +174,7 @@ class Project(BaseModel): return self @model_validator(mode="after") - def _ensure_indexed(self) -> "Project": + def _ensure_indexed(self) -> Project: """Wrap every collection in an :class:`_IndexedList` and drop caches. Runs after each validation pass (construction, ``model_validate``, @@ -223,7 +223,7 @@ class Project(BaseModel): # ─────────────────── invariants ─────────────────── @model_validator(mode="after") - def _check_ndm_ndf(self) -> "Project": + def _check_ndm_ndf(self) -> Project: valid = {(2, 2), (2, 3), (3, 3), (3, 6)} if (self.ndm, self.ndf) not in valid: raise ValueError( @@ -233,7 +233,7 @@ class Project(BaseModel): return self @model_validator(mode="after") - def _check_unique_ids(self) -> "Project": + def _check_unique_ids(self) -> Project: for label, items in ( ("node", self.nodes), ("material", self.materials), diff --git a/src/otko/core/sections/__init__.py b/src/otko/core/sections/__init__.py index 595b09b..530e0ca 100644 --- a/src/otko/core/sections/__init__.py +++ b/src/otko/core/sections/__init__.py @@ -16,7 +16,7 @@ exactly so the runner can emit them without further translation. from __future__ import annotations -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import BaseModel, ConfigDict, Field, PositiveFloat, PositiveInt @@ -61,7 +61,7 @@ class RectShape(BaseModel): SectionShape = Annotated[ - Union[PipeShape, AngleShape, RectShape], + PipeShape | AngleShape | RectShape, Field(discriminator="kind"), ] @@ -165,12 +165,12 @@ class StraightLayer(BaseModel): Patch = Annotated[ - Union[RectangularPatch, CircularPatch], + RectangularPatch | CircularPatch, Field(discriminator="kind"), ] Layer = Annotated[ - Union[StraightLayer], + StraightLayer, Field(discriminator="kind"), ] @@ -225,6 +225,6 @@ class SectionAggregator(Entity): Section = Annotated[ - Union[ElasticSection, FiberSection, SectionAggregator], + ElasticSection | FiberSection | SectionAggregator, Field(discriminator="type"), ] diff --git a/src/otko/services/animation_export.py b/src/otko/services/animation_export.py index 20c3f53..fa80790 100644 --- a/src/otko/services/animation_export.py +++ b/src/otko/services/animation_export.py @@ -12,8 +12,9 @@ the main thread. from __future__ import annotations import math +from collections.abc import Callable from pathlib import Path -from typing import Any, Callable +from typing import Any import numpy as np diff --git a/src/otko/services/export.py b/src/otko/services/export.py index 0f3a22c..5886360 100644 --- a/src/otko/services/export.py +++ b/src/otko/services/export.py @@ -238,7 +238,7 @@ def _record_calls(project: Project, case_id: int | None) -> list[RecordedCall]: runner.build() else: case = _find_case(project, case_id) - if isinstance(case, (StaticCase, ModalCase, PushoverCase)): + if isinstance(case, StaticCase | ModalCase | PushoverCase): # Safe to run end-to-end: dummy query results let the # harvesting code finish; queries are filtered at render. runner.run(case) diff --git a/src/otko/services/peer_record.py b/src/otko/services/peer_record.py index b6f6e6c..ce99a54 100644 --- a/src/otko/services/peer_record.py +++ b/src/otko/services/peer_record.py @@ -11,6 +11,7 @@ building the PathTimeSeries. from __future__ import annotations +import contextlib import re from pathlib import Path @@ -64,10 +65,9 @@ def parse_peer_record(path: str | Path) -> tuple[float, int, list[float]]: values: list[float] = [] for line in lines[data_start:]: for tok in line.split(): - try: + # Skip stray non-numeric tokens. + with contextlib.suppress(ValueError): values.append(float(tok)) - except ValueError: - pass # skip stray tokens if not values: raise ValueError("Header parsed but no numeric data lines found.") return dt, npts, values @@ -83,10 +83,8 @@ def parse_plain_values(path: str | Path) -> list[float]: vals: list[float] = [] for line in text.splitlines(): for tok in line.split(): - try: + with contextlib.suppress(ValueError): vals.append(float(tok)) - except ValueError: - pass if not vals: raise ValueError(f"{path} contains no numeric values.") return vals diff --git a/src/otko/services/section_bbox.py b/src/otko/services/section_bbox.py index b3438b8..18f5a20 100644 --- a/src/otko/services/section_bbox.py +++ b/src/otko/services/section_bbox.py @@ -54,7 +54,7 @@ def _angle_in_arc(angle: float, start: float, span: float) -> bool: def bbox_for_section( section: object, - project: "Project | None" = None, + project: Project | None = None, ) -> tuple[float, float] | None: """Return ``(width_y, height_z)`` of the section's local bounding box. diff --git a/src/otko/services/section_properties.py b/src/otko/services/section_properties.py index a38d4e4..a637428 100644 --- a/src/otko/services/section_properties.py +++ b/src/otko/services/section_properties.py @@ -21,7 +21,6 @@ import numpy as np from otko.core.sections import ( CircularPatch, FiberSection, - Fibre, RectangularPatch, StraightLayer, ) diff --git a/src/otko/viewmodels/project_vm.py b/src/otko/viewmodels/project_vm.py index 6ed5dbd..9ea06d1 100644 --- a/src/otko/viewmodels/project_vm.py +++ b/src/otko/viewmodels/project_vm.py @@ -11,6 +11,7 @@ a re-paint without losing camera/selection state. from __future__ import annotations +import contextlib from pathlib import Path from PySide6.QtCore import QObject, Signal, Slot @@ -40,10 +41,8 @@ class ProjectViewModel(QObject): @Slot(bool) def _on_stack_clean_changed(self, clean: bool) -> None: # Guard against the late-fire that Qt sends during destruction. - try: + with contextlib.suppress(RuntimeError): self._set_dirty(not clean) - except RuntimeError: - pass # ── read ───────────────────────────────────────────────────────── @property diff --git a/src/otko/views/action_handlers.py b/src/otko/views/action_handlers.py index bcd9305..4352b00 100644 --- a/src/otko/views/action_handlers.py +++ b/src/otko/views/action_handlers.py @@ -47,9 +47,9 @@ from otko.views.dialogs import ( AssignSectionDialog, AssignSupportDialog, AssignZeroLengthSectionDialog, - LoadCombinationManagerDialog, CoordinateGridSystemsDialog, LinearTimeSeriesDialog, + LoadCombinationManagerDialog, MaterialLibraryDialog, MaterialTesterDialog, MirrorDialog, diff --git a/src/otko/views/canvas3d/diagram_renderer.py b/src/otko/views/canvas3d/diagram_renderer.py index 28b6f82..05c5f2c 100644 --- a/src/otko/views/canvas3d/diagram_renderer.py +++ b/src/otko/views/canvas3d/diagram_renderer.py @@ -22,6 +22,7 @@ Convention: from __future__ import annotations +import contextlib import logging from typing import Any @@ -254,10 +255,8 @@ class DiagramRenderer: pass self._actor = None if self._label_actor is not None: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(self._label_actor, render=False) - except Exception: - pass self._label_actor = None # ── helpers ───────────────────────────────────────────────────── diff --git a/src/otko/views/canvas3d/model_renderer.py b/src/otko/views/canvas3d/model_renderer.py index 568c079..9eb9274 100644 --- a/src/otko/views/canvas3d/model_renderer.py +++ b/src/otko/views/canvas3d/model_renderer.py @@ -10,9 +10,10 @@ Mode-aware: MODEL / DEFORMED / MODAL change only the points array. from __future__ import annotations +import contextlib import enum from dataclasses import dataclass -from typing import Any +from typing import Any, ClassVar import numpy as np import pyvista as pv @@ -24,11 +25,11 @@ from otko.core import ( ElasticBeamColumn, ForceBeamColumn, NodalLoad, - QuadElement, - UniformElementLoad, PlainLoadPattern, Project, + QuadElement, TrussElement, + UniformElementLoad, ZeroLengthElement, ZeroLengthSectionElement, ) @@ -184,8 +185,8 @@ class ModelRenderer: r, g, b = (int(round(x * 255)) for x in rgb) return f"#{r:02x}{g:02x}{b:02x}" - _NODE_LUT = ["#d9d9d9", "#00ffff"] # gray normal, cyan selected - _FRAME_LUT = ["#338cd9", "#00ffff"] # blue normal, cyan selected + _NODE_LUT: ClassVar[list[str]] = ["#d9d9d9", "#00ffff"] # gray normal, cyan selected + _FRAME_LUT: ClassVar[list[str]] = ["#338cd9", "#00ffff"] # blue normal, cyan selected def __init__(self, plotter: Any, style: RenderStyle | None = None) -> None: self._plotter = plotter @@ -219,10 +220,8 @@ class ModelRenderer: # plan view at Z=3 doesn't see the Z=0 grid cluttering the view. self._working_plane: tuple[str, float] | None = None - try: + with contextlib.suppress(Exception): self._plotter.enable_anti_aliasing("ssaa") - except Exception: - pass # ── public API ─────────────────────────────────────────────────── def render(self, project: Project | None) -> None: @@ -329,10 +328,8 @@ class ModelRenderer: """ # Remove any previous marker. if self._hover_actor is not None: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(self._hover_actor, render=False) - except Exception: - pass self._hover_actor = None if world_point is None: @@ -376,10 +373,8 @@ class ModelRenderer: sphere = pv.Sphere(radius=radius, theta_resolution=8, phi_resolution=8) glyph = self._node_pd.glyph(geom=sphere, scale=False, orient=False) if self._node_actor is not None: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(self._node_actor, render=False) - except Exception: - pass self._node_glyph = glyph self._node_actor = self._plotter.add_mesh( glyph, @@ -484,10 +479,17 @@ class ModelRenderer: plane_axis = axis_idx plane_offset_local = plane_off - cs_shift - def _on_active_plane(local_pt: tuple[float, float, float]) -> bool: - if plane_axis is None: + # Bind the per-iteration plane state as defaults so the closure + # cannot read a later iteration's values (B023); the helper is + # only ever called from within this loop body. + def _on_active_plane( + local_pt: tuple[float, float, float], + _axis: int | None = plane_axis, + _offset: float | None = plane_offset_local, + ) -> bool: + if _axis is None or _offset is None: return True - return abs(local_pt[plane_axis] - plane_offset_local) < 1e-6 # type: ignore[operator] + return abs(local_pt[_axis] - _offset) < 1e-6 # Collect active + dim segments separately so they get their # own polydata + actor (different opacity / color). @@ -496,13 +498,21 @@ class ModelRenderer: dim_pts: list[tuple[float, float, float]] = [] dim_cells: list[int] = [] - def add_seg(p1: tuple[float, float, float], p2: tuple[float, float, float]) -> None: + def add_seg( + p1: tuple[float, float, float], + p2: tuple[float, float, float], + _active_pts: list[tuple[float, float, float]] = active_pts, + _active_cells: list[int] = active_cells, + _dim_pts: list[tuple[float, float, float]] = dim_pts, + _dim_cells: list[int] = dim_cells, + _cs: Any = cs, + ) -> None: on_active = _on_active_plane(p1) and _on_active_plane(p2) - bucket_pts = active_pts if on_active else dim_pts - bucket_cells = active_cells if on_active else dim_cells + bucket_pts = _active_pts if on_active else _dim_pts + bucket_cells = _active_cells if on_active else _dim_cells i = len(bucket_pts) - bucket_pts.append(cs.coord.local_to_world(p1)) - bucket_pts.append(cs.coord.local_to_world(p2)) + bucket_pts.append(_cs.coord.local_to_world(p1)) + bucket_pts.append(_cs.coord.local_to_world(p2)) bucket_cells.extend([2, i, i + 1]) z_planes = zs if zs else [0.0] @@ -614,7 +624,7 @@ class ModelRenderer: continue # By design: area-only / thickness-only / material-only # elements carry no section. - if isinstance(el, (TrussElement, CorotTrussElement, QuadElement, ZeroLengthElement)): + if isinstance(el, TrussElement | CorotTrussElement | QuadElement | ZeroLengthElement): continue node_i = node_by_id.get(el.nodes[0]) node_j = node_by_id.get(el.nodes[1]) @@ -665,18 +675,16 @@ class ModelRenderer: diag = self._diag_of_points(self._node_original_points) cap = max(diag * 0.08, 1e-6) for el in project.elements: - if isinstance(el, (QuadElement, ZeroLengthElement, ZeroLengthSectionElement)): + if isinstance(el, QuadElement | ZeroLengthElement | ZeroLengthSectionElement): continue if not isinstance( el, - ( - ElasticBeamColumn, - DispBeamColumn, - ForceBeamColumn, - BeamWithHingesElement, - TrussElement, - CorotTrussElement, - ), + ElasticBeamColumn + | DispBeamColumn + | ForceBeamColumn + | BeamWithHingesElement + | TrussElement + | CorotTrussElement, ): continue node_i = node_by_id.get(el.nodes[0]) @@ -1094,15 +1102,11 @@ class ModelRenderer: self._clear_label_actors() for a in (self._node_actor, self._frame_actor): if a is not None: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(a, render=False) - except Exception: - pass for a in self._aux_actors: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(a, render=False) - except Exception: - pass self._node_actor = None self._frame_actor = None self._aux_actors.clear() @@ -1120,10 +1124,8 @@ class ModelRenderer: def _clear_label_actors(self) -> None: for actor in (self._node_label_actor, self._element_label_actor): if actor is not None: - try: + with contextlib.suppress(Exception): self._plotter.remove_actor(actor, render=False) - except Exception: - pass self._node_label_actor = None self._element_label_actor = None diff --git a/src/otko/views/dialogs/assign_load.py b/src/otko/views/dialogs/assign_load.py index 9ab8c3c..534ba69 100644 --- a/src/otko/views/dialogs/assign_load.py +++ b/src/otko/views/dialogs/assign_load.py @@ -8,7 +8,6 @@ from PySide6.QtWidgets import ( QDialogButtonBox, QDoubleSpinBox, QFormLayout, - QInputDialog, QLabel, QLineEdit, QVBoxLayout, diff --git a/src/otko/views/dialogs/assign_masses.py b/src/otko/views/dialogs/assign_masses.py index 6592e44..f87a710 100644 --- a/src/otko/views/dialogs/assign_masses.py +++ b/src/otko/views/dialogs/assign_masses.py @@ -8,6 +8,8 @@ it maps cleanly onto our existing :class:`SetMassCommand`. from __future__ import annotations +import contextlib + from PySide6.QtWidgets import ( QCheckBox, QDialog, @@ -103,10 +105,8 @@ class AssignMassesDialog(QDialog): self._my.setValue(self._mx.value()) self._mx.valueChanged.connect(self._my.setValue) else: - try: + with contextlib.suppress(RuntimeError, TypeError): self._mx.valueChanged.disconnect(self._my.setValue) - except (RuntimeError, TypeError): - pass def mass_vector(self) -> tuple[float, float, float, float, float, float]: """Return the 6-tuple (Mx, My, Mz, Mxx, Myy, Mzz).""" diff --git a/src/otko/views/dialogs/assign_support.py b/src/otko/views/dialogs/assign_support.py index b742ebe..8b3a5bc 100644 --- a/src/otko/views/dialogs/assign_support.py +++ b/src/otko/views/dialogs/assign_support.py @@ -8,7 +8,6 @@ model's actual ``ndf`` at translation time. from __future__ import annotations -from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QButtonGroup, QCheckBox, @@ -22,7 +21,6 @@ from PySide6.QtWidgets import ( QWidget, ) - # Preset → 6-tuple (Ux, Uy, Uz, Rx, Ry, Rz) PRESETS: dict[str, tuple[bool, bool, bool, bool, bool, bool]] = { "Free": (False, False, False, False, False, False), diff --git a/src/otko/views/dialogs/case_forms.py b/src/otko/views/dialogs/case_forms.py index b8f7b24..d608992 100644 --- a/src/otko/views/dialogs/case_forms.py +++ b/src/otko/views/dialogs/case_forms.py @@ -21,7 +21,6 @@ from PySide6.QtWidgets import ( QListWidget, QListWidgetItem, QSpinBox, - QVBoxLayout, QWidget, ) diff --git a/src/otko/views/dialogs/case_manager.py b/src/otko/views/dialogs/case_manager.py index 000a90c..3ba5f29 100644 --- a/src/otko/views/dialogs/case_manager.py +++ b/src/otko/views/dialogs/case_manager.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING +from pydantic import ValidationError from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QDialog, @@ -19,7 +20,6 @@ from PySide6.QtWidgets import ( QVBoxLayout, QWidget, ) -from pydantic import ValidationError from otko.commands import ( AddAnalysisCasesCommand, @@ -34,7 +34,7 @@ from otko.core import ( TransientCase, ) from otko.viewmodels import ProjectViewModel -from otko.views.dialogs.case_forms import FORM_REGISTRY, form_for +from otko.views.dialogs.case_forms import form_for if TYPE_CHECKING: from otko.core import Project diff --git a/src/otko/views/dialogs/define_grid_data.py b/src/otko/views/dialogs/define_grid_data.py index 8230650..7440b04 100644 --- a/src/otko/views/dialogs/define_grid_data.py +++ b/src/otko/views/dialogs/define_grid_data.py @@ -18,6 +18,8 @@ Top-level controls: from __future__ import annotations +from typing import ClassVar + from PySide6.QtCore import Qt from PySide6.QtGui import QColor from PySide6.QtWidgets import ( @@ -27,7 +29,6 @@ from PySide6.QtWidgets import ( QComboBox, QDialog, QDialogButtonBox, - QDoubleSpinBox, QFormLayout, QGroupBox, QHBoxLayout, @@ -52,7 +53,6 @@ from otko.core.geometry import ( GridSystem, ) - _LINE_TYPE_CHOICES = ["Primary", "Secondary"] _BUBBLE_LOC_CHOICES = ["Start", "End"] @@ -60,7 +60,14 @@ _BUBBLE_LOC_CHOICES = ["Start", "End"] class _AxisGridTable(QWidget): """A single X/Y/Z spreadsheet — one row per GridLine.""" - COLUMNS = ["Grid ID", "Ordinate", "Line Type", "Visibility", "Bubble Loc", "Color"] + COLUMNS: ClassVar[list[str]] = [ + "Grid ID", + "Ordinate", + "Line Type", + "Visibility", + "Bubble Loc", + "Color", + ] def __init__(self, axis: str, parent: QWidget | None = None) -> None: super().__init__(parent) diff --git a/src/otko/views/dialogs/grid_system.py b/src/otko/views/dialogs/grid_system.py index 3f39cf8..c1ac05c 100644 --- a/src/otko/views/dialogs/grid_system.py +++ b/src/otko/views/dialogs/grid_system.py @@ -17,8 +17,8 @@ from PySide6.QtWidgets import ( QDialog, QDialogButtonBox, QFormLayout, - QLineEdit, QLabel, + QLineEdit, QVBoxLayout, QWidget, ) diff --git a/src/otko/views/dialogs/locate_origin.py b/src/otko/views/dialogs/locate_origin.py index 21708e7..28a8205 100644 --- a/src/otko/views/dialogs/locate_origin.py +++ b/src/otko/views/dialogs/locate_origin.py @@ -13,7 +13,6 @@ from PySide6.QtWidgets import ( QDoubleSpinBox, QFormLayout, QGroupBox, - QHBoxLayout, QLabel, QVBoxLayout, QWidget, diff --git a/src/otko/views/dialogs/material_library.py b/src/otko/views/dialogs/material_library.py index ace776e..371a507 100644 --- a/src/otko/views/dialogs/material_library.py +++ b/src/otko/views/dialogs/material_library.py @@ -7,9 +7,9 @@ committed. Each Add/Edit/Delete is its own undoable step. from __future__ import annotations +from pydantic import ValidationError from PySide6.QtCore import Qt from PySide6.QtWidgets import ( - QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, @@ -23,7 +23,6 @@ from PySide6.QtWidgets import ( QVBoxLayout, QWidget, ) -from pydantic import ValidationError from otko.commands import ( AddMaterialsCommand, diff --git a/src/otko/views/dialogs/path_time_series.py b/src/otko/views/dialogs/path_time_series.py index 476b5d4..66a6b34 100644 --- a/src/otko/views/dialogs/path_time_series.py +++ b/src/otko/views/dialogs/path_time_series.py @@ -120,7 +120,7 @@ class PathTimeSeriesDialog(QDialog): return try: dt, npts, vals = parse_peer_record(fname) - except Exception as exc: # noqa: BLE001 + except Exception as exc: QMessageBox.critical(self, "PEER import failed", str(exc)) return self._values = vals @@ -142,7 +142,7 @@ class PathTimeSeriesDialog(QDialog): return try: vals = parse_plain_values(fname) - except Exception as exc: # noqa: BLE001 + except Exception as exc: QMessageBox.critical(self, "Import failed", str(exc)) return self._values = vals diff --git a/src/otko/views/dialogs/quick_grid_lines.py b/src/otko/views/dialogs/quick_grid_lines.py index 23d3b61..7928d60 100644 --- a/src/otko/views/dialogs/quick_grid_lines.py +++ b/src/otko/views/dialogs/quick_grid_lines.py @@ -11,7 +11,6 @@ from PySide6.QtWidgets import ( QDialog, QDialogButtonBox, QDoubleSpinBox, - QFormLayout, QGroupBox, QHBoxLayout, QLabel, diff --git a/src/otko/views/dialogs/run_analysis.py b/src/otko/views/dialogs/run_analysis.py index f31ec6f..b1beb12 100644 --- a/src/otko/views/dialogs/run_analysis.py +++ b/src/otko/views/dialogs/run_analysis.py @@ -193,7 +193,7 @@ class RunAnalysisDialog(QDialog): results_dir = self._vm.path.parent / f"{self._vm.path.stem}_results" try: self._runner.run(self._vm.project, case, results_dir=results_dir) - except Exception as exc: # noqa: BLE001 + except Exception as exc: self._log.appendPlainText(f"Could not start: {exc}") def _on_evaluate_combo(self) -> None: @@ -233,7 +233,7 @@ class RunAnalysisDialog(QDialog): self._results = combined self._log.appendPlainText(f"--- Done. Returned {type(combined).__name__}. ---") self._runner.finished.emit(combined) - except Exception as exc: # noqa: BLE001 + except Exception as exc: import traceback self._log.appendPlainText("--- FAILED ---") diff --git a/src/otko/views/dialogs/section_editor.py b/src/otko/views/dialogs/section_editor.py index 4bb185f..fdb9031 100644 --- a/src/otko/views/dialogs/section_editor.py +++ b/src/otko/views/dialogs/section_editor.py @@ -12,7 +12,6 @@ from __future__ import annotations from typing import Any -import numpy as np import pyqtgraph as pg from PySide6.QtCore import Qt from PySide6.QtWidgets import ( @@ -324,7 +323,6 @@ class FiberSectionEditor(QDialog): self._props_label.setText("Add patches or layers to see the preview.") return - f = props.fibre_yz # Color by material: assign a palette index per unique material_id. mat_ids = [] for p in self._patches: diff --git a/src/otko/views/dialogs/section_library.py b/src/otko/views/dialogs/section_library.py index c525a50..198c821 100644 --- a/src/otko/views/dialogs/section_library.py +++ b/src/otko/views/dialogs/section_library.py @@ -2,6 +2,7 @@ from __future__ import annotations +from pydantic import ValidationError from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QDialog, @@ -17,7 +18,6 @@ from PySide6.QtWidgets import ( QVBoxLayout, QWidget, ) -from pydantic import ValidationError from otko.commands import ( AddSectionsCommand, diff --git a/src/otko/views/dialogs/uniform_excitation.py b/src/otko/views/dialogs/uniform_excitation.py index 6bdc94b..b7cc471 100644 --- a/src/otko/views/dialogs/uniform_excitation.py +++ b/src/otko/views/dialogs/uniform_excitation.py @@ -23,7 +23,6 @@ from PySide6.QtWidgets import ( from otko.core import Project, UniformExcitationPattern - _DIRECTION_CHOICES: list[tuple[int, str]] = [ (1, "1 — X (horizontal)"), (2, "2 — Y (vertical for ndm=2, lateral for ndm=3)"), diff --git a/src/otko/views/docks/deformed_shape.py b/src/otko/views/docks/deformed_shape.py index 497dc85..de89664 100644 --- a/src/otko/views/docks/deformed_shape.py +++ b/src/otko/views/docks/deformed_shape.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import ( diff --git a/src/otko/views/docks/force_diagram.py b/src/otko/views/docks/force_diagram.py index 2580e36..a19e99d 100644 --- a/src/otko/views/docks/force_diagram.py +++ b/src/otko/views/docks/force_diagram.py @@ -10,8 +10,6 @@ Exposes two signals: from __future__ import annotations -import math - from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import ( QComboBox, diff --git a/src/otko/views/docks/hysteresis.py b/src/otko/views/docks/hysteresis.py index cc56c3e..c8f0a79 100644 --- a/src/otko/views/docks/hysteresis.py +++ b/src/otko/views/docks/hysteresis.py @@ -15,6 +15,7 @@ For 2D it's [N1, Vy1, Mz1, N2, Vy2, Mz2]. from __future__ import annotations +import contextlib from typing import Any import pyqtgraph as pg @@ -337,8 +338,6 @@ class HysteresisView(QWidget): def _clear_curve(self) -> None: if self._curve is not None: - try: + with contextlib.suppress(Exception): self._plot.removeItem(self._curve) - except Exception: - pass self._curve = None diff --git a/src/otko/views/docks/property_editor.py b/src/otko/views/docks/property_editor.py index 10cb7f7..b6c5ed0 100644 --- a/src/otko/views/docks/property_editor.py +++ b/src/otko/views/docks/property_editor.py @@ -8,9 +8,8 @@ dumb: no direct Project mutation, no Qt ↔ OpenSees coupling. from __future__ import annotations -from typing import Callable +from collections.abc import Callable -from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QComboBox, QDoubleSpinBox, @@ -26,7 +25,6 @@ from PySide6.QtWidgets import ( from otko.core import Project - # Element types the Properties dock lets the user switch between. # "ElasticBeamColumn" needs a section; "Truss" / "CorotTruss" need a # uniaxial material + area. The Convert command drops/adds fields to diff --git a/src/otko/views/docks/pushover_curve.py b/src/otko/views/docks/pushover_curve.py index 3dc0e11..86beff2 100644 --- a/src/otko/views/docks/pushover_curve.py +++ b/src/otko/views/docks/pushover_curve.py @@ -26,7 +26,7 @@ from PySide6.QtWidgets import ( QWidget, ) -from otko.core import UnitLabels, UnitSystem, labels_for +from otko.core import UnitSystem, labels_for from otko.services.results import PushoverResults if TYPE_CHECKING: diff --git a/src/otko/views/docks/response_spectrum.py b/src/otko/views/docks/response_spectrum.py index 42cb523..7664171 100644 --- a/src/otko/views/docks/response_spectrum.py +++ b/src/otko/views/docks/response_spectrum.py @@ -8,7 +8,7 @@ Two panels: from __future__ import annotations import pyqtgraph as pg -from PySide6.QtCore import Signal +from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import ( QHBoxLayout, QHeaderView, @@ -20,7 +20,6 @@ from PySide6.QtWidgets import ( QVBoxLayout, QWidget, ) -from PySide6.QtCore import Qt from otko.core import ResponseSpectrum from otko.services.results import ResponseSpectrumResults diff --git a/src/otko/views/docks/table_dock.py b/src/otko/views/docks/table_dock.py index c8d3342..3af8383 100644 --- a/src/otko/views/docks/table_dock.py +++ b/src/otko/views/docks/table_dock.py @@ -197,7 +197,7 @@ def _fmt(value: Any) -> str: return "—" if isinstance(value, float): return f"{value:g}" - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): return ", ".join(_fmt(v) for v in value) return str(value) @@ -1035,7 +1035,7 @@ class TableDock(QWidget): try: key = int(pid) label = name_of.get(key, str(key)) or str(key) - parts.append(f"{label}×{float(factor):g}") # noqa: RUF001 + parts.append(f"{label}×{float(factor):g}") except (TypeError, ValueError): continue return ", ".join(parts) if parts else "—" diff --git a/src/otko/views/docks/time_history.py b/src/otko/views/docks/time_history.py index ba285fe..560dba0 100644 --- a/src/otko/views/docks/time_history.py +++ b/src/otko/views/docks/time_history.py @@ -10,9 +10,9 @@ runner to record additional series (planned for a later phase). from __future__ import annotations +import contextlib from typing import Any -import numpy as np import pyqtgraph as pg from PySide6.QtCore import Signal from PySide6.QtWidgets import ( @@ -208,9 +208,7 @@ class TimeHistoryView(QWidget): def _clear_traces(self) -> None: for _, _, item in self._traces: - try: + with contextlib.suppress(Exception): self._plot.removeItem(item) - except Exception: - pass self._traces.clear() self._trace_list.clear() diff --git a/src/otko/views/main_window.py b/src/otko/views/main_window.py index ec9f487..958459e 100644 --- a/src/otko/views/main_window.py +++ b/src/otko/views/main_window.py @@ -105,7 +105,7 @@ class MainWindow( if state is not None: self.restoreState(state) - def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802 (Qt override) + def closeEvent(self, event: QCloseEvent) -> None: # (Qt override) """Prompt to save unsaved work, then persist the window layout. The prompt is shown only for a visible window: Qt also closes hidden diff --git a/src/otko/views/tools/base.py b/src/otko/views/tools/base.py index 04abc8e..bfb6072 100644 --- a/src/otko/views/tools/base.py +++ b/src/otko/views/tools/base.py @@ -35,8 +35,8 @@ class CanvasTool(QObject): def __init__( self, - canvas: "ModelCanvas", - vm: "ProjectViewModel", + canvas: ModelCanvas, + vm: ProjectViewModel, parent: QObject | None = None, ) -> None: super().__init__(parent) @@ -87,7 +87,7 @@ class ToolController(QObject): toolChanged = Signal(object) # emits the new CanvasTool (or None for default) def __init__( - self, canvas: "ModelCanvas", vm: "ProjectViewModel", parent: QObject | None = None + self, canvas: ModelCanvas, vm: ProjectViewModel, parent: QObject | None = None ) -> None: super().__init__(parent) self._canvas = canvas diff --git a/src/otko/views/tools/draw_frame.py b/src/otko/views/tools/draw_frame.py index 958abe3..980716a 100644 --- a/src/otko/views/tools/draw_frame.py +++ b/src/otko/views/tools/draw_frame.py @@ -41,9 +41,9 @@ class DrawFrameTool(LinearElementDrawTool): def __init__( self, - canvas: "ModelCanvas", - vm: "ProjectViewModel", - parent: "QObject | None" = None, + canvas: ModelCanvas, + vm: ProjectViewModel, + parent: QObject | None = None, ) -> None: super().__init__(canvas, vm, parent) diff --git a/src/otko/views/tools/draw_node.py b/src/otko/views/tools/draw_node.py index 5e7ee0f..0f286be 100644 --- a/src/otko/views/tools/draw_node.py +++ b/src/otko/views/tools/draw_node.py @@ -115,9 +115,9 @@ class DrawNodeTool(CanvasTool): def __init__( self, - canvas: "ModelCanvas", - vm: "ProjectViewModel", - parent: "QObject | None" = None, + canvas: ModelCanvas, + vm: ProjectViewModel, + parent: QObject | None = None, ) -> None: super().__init__(canvas, vm, parent) diff --git a/src/otko/views/tools/draw_truss.py b/src/otko/views/tools/draw_truss.py index 1eeb69d..4ee8254 100644 --- a/src/otko/views/tools/draw_truss.py +++ b/src/otko/views/tools/draw_truss.py @@ -37,9 +37,9 @@ class DrawTrussTool(LinearElementDrawTool): def __init__( self, - canvas: "ModelCanvas", - vm: "ProjectViewModel", - parent: "QObject | None" = None, + canvas: ModelCanvas, + vm: ProjectViewModel, + parent: QObject | None = None, ) -> None: super().__init__(canvas, vm, parent) diff --git a/tests/gui/test_analysis_commands.py b/tests/gui/test_analysis_commands.py index 9750537..fed67e3 100644 --- a/tests/gui/test_analysis_commands.py +++ b/tests/gui/test_analysis_commands.py @@ -6,12 +6,12 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddAnalysisCasesCommand, DeleteAnalysisCasesCommand, UpdateAnalysisCaseCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( LinearTimeSeries, ModalCase, NodalLoad, @@ -20,7 +20,7 @@ from otko.core import ( # noqa: E402 StaticCase, TransientCase, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel def _vm_with_pattern() -> ProjectViewModel: diff --git a/tests/gui/test_analysis_runner.py b/tests/gui/test_analysis_runner.py index 2d19389..14c100a 100644 --- a/tests/gui/test_analysis_runner.py +++ b/tests/gui/test_analysis_runner.py @@ -12,21 +12,21 @@ import pytest pytest.importorskip("PySide6") pytest.importorskip("openseespy.opensees") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddAnalysisCasesCommand, AddElementsCommand, AddNodalLoadsCommand, AddNodesCommand, AddSectionsCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, Node, StaticCase, ) -from otko.services.results import StaticResults # noqa: E402 -from otko.viewmodels import AnalysisRunner, ProjectViewModel # noqa: E402 +from otko.services.results import StaticResults +from otko.viewmodels import AnalysisRunner, ProjectViewModel @pytest.fixture diff --git a/tests/gui/test_assign_load_pattern_name.py b/tests/gui/test_assign_load_pattern_name.py index 0f95da2..b0b4fba 100644 --- a/tests/gui/test_assign_load_pattern_name.py +++ b/tests/gui/test_assign_load_pattern_name.py @@ -6,15 +6,14 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import AddNodalLoadsCommand # noqa: E402 -from otko.core import ( # noqa: E402 +from otko.commands import AddNodalLoadsCommand +from otko.core import ( LinearTimeSeries, Node, PlainLoadPattern, - Project, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 -from otko.views.dialogs.assign_load import AssignLoadDialog # noqa: E402 +from otko.viewmodels import ProjectViewModel +from otko.views.dialogs.assign_load import AssignLoadDialog @pytest.mark.gui diff --git a/tests/gui/test_assign_masses.py b/tests/gui/test_assign_masses.py index 87a3a7a..3ba0dda 100644 --- a/tests/gui/test_assign_masses.py +++ b/tests/gui/test_assign_masses.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.views.dialogs.assign_masses import AssignMassesDialog # noqa: E402 +from otko.views.dialogs.assign_masses import AssignMassesDialog @pytest.mark.gui diff --git a/tests/gui/test_assign_zls.py b/tests/gui/test_assign_zls.py index 850e0a8..9a8cb66 100644 --- a/tests/gui/test_assign_zls.py +++ b/tests/gui/test_assign_zls.py @@ -6,12 +6,12 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticSection, Node, Project, ) -from otko.views.dialogs.assign_zls import ( # noqa: E402 +from otko.views.dialogs.assign_zls import ( AssignZeroLengthSectionDialog, ) diff --git a/tests/gui/test_canvas_grid_snap.py b/tests/gui/test_canvas_grid_snap.py index 8410edf..02ddb4b 100644 --- a/tests/gui/test_canvas_grid_snap.py +++ b/tests/gui/test_canvas_grid_snap.py @@ -12,7 +12,7 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( CoordinateGridSystem, CoordinateSystem, GridSystem, diff --git a/tests/gui/test_case_forms.py b/tests/gui/test_case_forms.py index c911141..6672806 100644 --- a/tests/gui/test_case_forms.py +++ b/tests/gui/test_case_forms.py @@ -6,13 +6,13 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( NodalLoad, PlainLoadPattern, StaticCase, TransientCase, ) -from otko.views.dialogs.case_forms import TransientCaseForm # noqa: E402 +from otko.views.dialogs.case_forms import TransientCaseForm def _patterns(): # type: ignore[no-untyped-def] diff --git a/tests/gui/test_commands.py b/tests/gui/test_commands.py index 4584ed9..0457390 100644 --- a/tests/gui/test_commands.py +++ b/tests/gui/test_commands.py @@ -11,15 +11,14 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddElementsCommand, AddNodalLoadsCommand, AddNodesCommand, - DeleteElementsCommand, DeleteNodesCommand, SetRestraintCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( LinearTimeSeries, NodalLoad, Node, @@ -27,7 +26,7 @@ from otko.core import ( # noqa: E402 Steel01, TrussElement, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel # ─────────────────────────── helpers ──────────────────────────────── diff --git a/tests/gui/test_convert_element_type.py b/tests/gui/test_convert_element_type.py index 4fa11ff..426c23b 100644 --- a/tests/gui/test_convert_element_type.py +++ b/tests/gui/test_convert_element_type.py @@ -6,21 +6,21 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddElementsCommand, AddMaterialsCommand, AddNodesCommand, AddSectionsCommand, ConvertElementTypeCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, ElasticUniaxial, Node, TrussElement, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel def _vm_setup() -> ProjectViewModel: diff --git a/tests/gui/test_define_dialogs.py b/tests/gui/test_define_dialogs.py index 57c1bfc..4b6f9b3 100644 --- a/tests/gui/test_define_dialogs.py +++ b/tests/gui/test_define_dialogs.py @@ -6,9 +6,9 @@ import pytest pytest.importorskip("PySide6") -from otko.core import LinearTimeSeries, Project # noqa: E402 -from otko.views.dialogs.linear_time_series import LinearTimeSeriesDialog # noqa: E402 -from otko.views.dialogs.plain_pattern import PlainPatternDialog # noqa: E402 +from otko.core import LinearTimeSeries, Project +from otko.views.dialogs.linear_time_series import LinearTimeSeriesDialog +from otko.views.dialogs.plain_pattern import PlainPatternDialog @pytest.mark.gui diff --git a/tests/gui/test_draw_frame_tool.py b/tests/gui/test_draw_frame_tool.py index 6161eb2..ff6370c 100644 --- a/tests/gui/test_draw_frame_tool.py +++ b/tests/gui/test_draw_frame_tool.py @@ -11,11 +11,11 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import AddNodesCommand # noqa: E402 -from otko.core import ElasticBeamColumn, ElasticSection, Node # noqa: E402 -from otko.viewmodels import ProjectViewModel # noqa: E402 -from otko.views.canvas3d import SelectionState # noqa: E402 -from otko.views.tools.draw_frame import DrawFrameTool # noqa: E402 +from otko.commands import AddNodesCommand +from otko.core import ElasticBeamColumn, ElasticSection, Node +from otko.viewmodels import ProjectViewModel +from otko.views.canvas3d import SelectionState +from otko.views.tools.draw_frame import DrawFrameTool class _CanvasStub: diff --git a/tests/gui/test_draw_node_tool.py b/tests/gui/test_draw_node_tool.py index d7d0023..aab389d 100644 --- a/tests/gui/test_draw_node_tool.py +++ b/tests/gui/test_draw_node_tool.py @@ -10,10 +10,10 @@ import pytest pytest.importorskip("PySide6") -from otko.core import GridSystem, Node, Project # noqa: E402 -from otko.viewmodels import ProjectViewModel # noqa: E402 -from otko.views.canvas3d.selection import SelectionState # noqa: E402 -from otko.views.tools.draw_node import ( # noqa: E402 +from otko.core import GridSystem, Node +from otko.viewmodels import ProjectViewModel +from otko.views.canvas3d.selection import SelectionState +from otko.views.tools.draw_node import ( DrawNodeTool, _snap_to_grid, ) diff --git a/tests/gui/test_draw_truss_tool.py b/tests/gui/test_draw_truss_tool.py index 4c67164..bcc2879 100644 --- a/tests/gui/test_draw_truss_tool.py +++ b/tests/gui/test_draw_truss_tool.py @@ -6,18 +6,17 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( CoordinateGridSystem, - CoordinateSystem, ElasticUniaxial, GridSystem, Node, TrussElement, make_grid_lines, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 -from otko.views.canvas3d.selection import SelectionState # noqa: E402 -from otko.views.tools.draw_truss import DrawTrussTool # noqa: E402 +from otko.viewmodels import ProjectViewModel +from otko.views.canvas3d.selection import SelectionState +from otko.views.tools.draw_truss import DrawTrussTool class _CanvasStub: diff --git a/tests/gui/test_grid_dialog.py b/tests/gui/test_grid_dialog.py index c73e852..0013ad4 100644 --- a/tests/gui/test_grid_dialog.py +++ b/tests/gui/test_grid_dialog.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( CoordinateGridSystem, CoordinateSystem, GridLine, diff --git a/tests/gui/test_material_dialog_concrete04.py b/tests/gui/test_material_dialog_concrete04.py index 4dd34d5..23523bc 100644 --- a/tests/gui/test_material_dialog_concrete04.py +++ b/tests/gui/test_material_dialog_concrete04.py @@ -6,10 +6,10 @@ import pytest pytest.importorskip("PySide6") -from otko.core import Concrete04 # noqa: E402 -from otko.views.dialogs.material_forms import ( # noqa: E402 - Concrete04Form, +from otko.core import Concrete04 +from otko.views.dialogs.material_forms import ( FORM_REGISTRY, + Concrete04Form, ) diff --git a/tests/gui/test_material_form_ranges.py b/tests/gui/test_material_form_ranges.py index cb47096..54c1899 100644 --- a/tests/gui/test_material_form_ranges.py +++ b/tests/gui/test_material_form_ranges.py @@ -10,7 +10,7 @@ import pytest pytest.importorskip("PySide6") -from otko.views.dialogs.material_forms import ( # noqa: E402 +from otko.views.dialogs.material_forms import ( Concrete01Form, Concrete02Form, Steel01Form, diff --git a/tests/gui/test_path_ts_dialogs.py b/tests/gui/test_path_ts_dialogs.py index 7245068..5f1e132 100644 --- a/tests/gui/test_path_ts_dialogs.py +++ b/tests/gui/test_path_ts_dialogs.py @@ -8,17 +8,16 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddLoadPatternCommand, AddTimeSeriesCommand, ) -from otko.core import ( # noqa: E402 - Node, +from otko.core import ( PathTimeSeries, Project, UniformExcitationPattern, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel # ─────────────── AddTimeSeriesCommand / AddLoadPatternCommand ────────────── diff --git a/tests/gui/test_property_commands.py b/tests/gui/test_property_commands.py index a325b87..8a49e26 100644 --- a/tests/gui/test_property_commands.py +++ b/tests/gui/test_property_commands.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddElementsCommand, AddMaterialsCommand, AddNodesCommand, @@ -16,7 +16,7 @@ from otko.commands import ( # noqa: E402 UpdateMaterialCommand, UpdateSectionCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, Node, @@ -24,7 +24,7 @@ from otko.core import ( # noqa: E402 Steel02, TrussElement, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel def _vm_with_steel() -> ProjectViewModel: diff --git a/tests/gui/test_property_forms.py b/tests/gui/test_property_forms.py index 3943498..aed5ece 100644 --- a/tests/gui/test_property_forms.py +++ b/tests/gui/test_property_forms.py @@ -10,13 +10,10 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 - Concrete01, +from otko.core import ( Concrete02, ElasticIsotropic, - ElasticPP, ElasticSection, - ElasticUniaxial, Steel01, Steel02, ) @@ -100,10 +97,14 @@ def test_elastic_section_form_round_trip(qtbot) -> None: # type: ignore[no-unty def test_form_for_dispatches_by_type(qtbot) -> None: # type: ignore[no-untyped-def] from otko.views.dialogs.material_forms import ( Steel01Form, + ) + from otko.views.dialogs.material_forms import ( form_for as material_form_for, ) from otko.views.dialogs.section_forms import ( ElasticSectionForm, + ) + from otko.views.dialogs.section_forms import ( form_for as section_form_for, ) diff --git a/tests/gui/test_pushover_curve_units.py b/tests/gui/test_pushover_curve_units.py index 03af4bb..9aee331 100644 --- a/tests/gui/test_pushover_curve_units.py +++ b/tests/gui/test_pushover_curve_units.py @@ -7,9 +7,9 @@ import pytest pytest.importorskip("PySide6") -from otko.core import UnitSystem # noqa: E402 -from otko.services.results import PushoverResults # noqa: E402 -from otko.views.docks.pushover_curve import ( # noqa: E402 +from otko.core import UnitSystem +from otko.services.results import PushoverResults +from otko.views.docks.pushover_curve import ( PushoverCurveView, _is_rotation_dof, ) diff --git a/tests/gui/test_run_analysis.py b/tests/gui/test_run_analysis.py index 03319e5..5281fd8 100644 --- a/tests/gui/test_run_analysis.py +++ b/tests/gui/test_run_analysis.py @@ -8,11 +8,11 @@ import pytest pytest.importorskip("PySide6") -from PySide6.QtCore import QObject, Signal # noqa: E402 +from PySide6.QtCore import QObject, Signal -from otko.core import LinearTimeSeries, PlainLoadPattern, Project, TransientCase # noqa: E402 -from otko.viewmodels import ProjectViewModel # noqa: E402 -from otko.views.dialogs.run_analysis import RunAnalysisDialog # noqa: E402 +from otko.core import LinearTimeSeries, PlainLoadPattern, TransientCase +from otko.viewmodels import ProjectViewModel +from otko.views.dialogs.run_analysis import RunAnalysisDialog class _FakeRunner(QObject): diff --git a/tests/gui/test_section_extrusion.py b/tests/gui/test_section_extrusion.py index ad21da0..3f66eb4 100644 --- a/tests/gui/test_section_extrusion.py +++ b/tests/gui/test_section_extrusion.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, Node, diff --git a/tests/gui/test_section_forms.py b/tests/gui/test_section_forms.py index 3c52f4d..4747cec 100644 --- a/tests/gui/test_section_forms.py +++ b/tests/gui/test_section_forms.py @@ -7,14 +7,14 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( AggregatorDOF, ElasticSection, FiberSection, RectangularPatch, SectionAggregator, ) -from otko.views.dialogs.section_forms import ( # noqa: E402 +from otko.views.dialogs.section_forms import ( FORM_REGISTRY, form_for, ) diff --git a/tests/gui/test_selection.py b/tests/gui/test_selection.py index c795493..365c03c 100644 --- a/tests/gui/test_selection.py +++ b/tests/gui/test_selection.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.views.canvas3d import SelectionState # noqa: E402 +from otko.views.canvas3d import SelectionState @pytest.mark.gui diff --git a/tests/gui/test_transforms.py b/tests/gui/test_transforms.py index 4ad8716..fe13b70 100644 --- a/tests/gui/test_transforms.py +++ b/tests/gui/test_transforms.py @@ -6,15 +6,15 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddElementsCommand, AddNodesCommand, MirrorCommand, MoveNodesCommand, ReplicateCommand, ) -from otko.core import Node, Steel01, TrussElement # noqa: E402 -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.core import Node, Steel01, TrussElement +from otko.viewmodels import ProjectViewModel def _populated_vm() -> ProjectViewModel: diff --git a/tests/gui/test_tree_multi_select.py b/tests/gui/test_tree_multi_select.py index 9048600..235ee7e 100644 --- a/tests/gui/test_tree_multi_select.py +++ b/tests/gui/test_tree_multi_select.py @@ -6,10 +6,9 @@ import pytest pytest.importorskip("PySide6") -from PySide6.QtCore import Qt # noqa: E402 -from PySide6.QtWidgets import QTreeWidget # noqa: E402 +from PySide6.QtWidgets import QTreeWidget -from otko.core import Node # noqa: E402 +from otko.core import Node @pytest.mark.gui diff --git a/tests/gui/test_units_status_bar.py b/tests/gui/test_units_status_bar.py index b052ad4..4eaaae1 100644 --- a/tests/gui/test_units_status_bar.py +++ b/tests/gui/test_units_status_bar.py @@ -6,7 +6,7 @@ import pytest pytest.importorskip("PySide6") -from otko.core import UnitSystem # noqa: E402 +from otko.core import UnitSystem @pytest.mark.gui diff --git a/tests/gui/test_update_element_fields.py b/tests/gui/test_update_element_fields.py index bdecc06..f83cd67 100644 --- a/tests/gui/test_update_element_fields.py +++ b/tests/gui/test_update_element_fields.py @@ -6,18 +6,18 @@ import pytest pytest.importorskip("PySide6") -from otko.commands import ( # noqa: E402 +from otko.commands import ( AddElementsCommand, AddMaterialsCommand, AddNodesCommand, UpdateElementFieldsCommand, ) -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticUniaxial, Node, TrussElement, ) -from otko.viewmodels import ProjectViewModel # noqa: E402 +from otko.viewmodels import ProjectViewModel def _vm_with_truss() -> ProjectViewModel: diff --git a/tests/gui/test_working_plane.py b/tests/gui/test_working_plane.py index a0b59f0..012c1c0 100644 --- a/tests/gui/test_working_plane.py +++ b/tests/gui/test_working_plane.py @@ -7,7 +7,7 @@ import pytest pytest.importorskip("PySide6") -from otko.core import ( # noqa: E402 +from otko.core import ( CoordinateGridSystem, GridSystem, Project, diff --git a/tests/integration/test_basic_truss.py b/tests/integration/test_basic_truss.py index 72ffaa3..20be03c 100644 --- a/tests/integration/test_basic_truss.py +++ b/tests/integration/test_basic_truss.py @@ -11,7 +11,7 @@ import pytest pytest.importorskip("openseespy") -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -25,7 +25,7 @@ def test_basic_truss_matches_opensees_tcl_reference() -> None: a linear rescale of the Tcl model — any deviation would flag a real solver/translation issue. """ - from examples.basic_truss import build_basic_truss, IN_TO_M + from examples.basic_truss import IN_TO_M, build_basic_truss proj = build_basic_truss() runner = OpenSeesRunner(proj) @@ -45,6 +45,7 @@ def test_basic_truss_matches_opensees_tcl_reference() -> None: def test_basic_truss_round_trips(tmp_path) -> None: # type: ignore[no-untyped-def] """The example project must survive save/load without any information loss.""" from examples.basic_truss import build_basic_truss + from otko.services import load_project, save_project p = build_basic_truss() diff --git a/tests/integration/test_beam_quad_2d.py b/tests/integration/test_beam_quad_2d.py index 6c52b5b..4dc4a60 100644 --- a/tests/integration/test_beam_quad_2d.py +++ b/tests/integration/test_beam_quad_2d.py @@ -9,8 +9,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_combinations.py b/tests/integration/test_combinations.py index 8a4dc21..8ba5ca7 100644 --- a/tests/integration/test_combinations.py +++ b/tests/integration/test_combinations.py @@ -8,7 +8,7 @@ import pytest ops = pytest.importorskip("openseespy.opensees") # skip if OpenSeesPy not installed -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, LinearTimeSeries, @@ -20,8 +20,8 @@ from otko.core import ( # noqa: E402 Project, StaticCase, ) -from otko.services import OpenSeesRunner, evaluate_combination # noqa: E402 -from otko.services.results import StaticResults # noqa: E402 +from otko.services import OpenSeesRunner, evaluate_combination +from otko.services.results import StaticResults pytestmark = pytest.mark.slow diff --git a/tests/integration/test_concrete04_runner.py b/tests/integration/test_concrete04_runner.py index 054f462..4293dea 100644 --- a/tests/integration/test_concrete04_runner.py +++ b/tests/integration/test_concrete04_runner.py @@ -18,7 +18,7 @@ import pytest pytest.importorskip("openseespy") -from otko.core import ( # noqa: E402 +from otko.core import ( Concrete04, FiberSection, ForceBeamColumn, @@ -32,7 +32,7 @@ from otko.core import ( # noqa: E402 StaticCase, UnitSystem, ) -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_dof_coverage.py b/tests/integration/test_dof_coverage.py index cb20abb..7e9e37e 100644 --- a/tests/integration/test_dof_coverage.py +++ b/tests/integration/test_dof_coverage.py @@ -11,7 +11,7 @@ import pytest pytest.importorskip("openseespy") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticUniaxial, LinearTimeSeries, NodalLoad, @@ -21,7 +21,7 @@ from otko.core import ( # noqa: E402 StaticCase, TrussElement, ) -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_eigen_two_storey_one_bay_frame.py b/tests/integration/test_eigen_two_storey_one_bay_frame.py index 49eb4cd..83cd67e 100644 --- a/tests/integration/test_eigen_two_storey_one_bay_frame.py +++ b/tests/integration/test_eigen_two_storey_one_bay_frame.py @@ -8,9 +8,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import ModalCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import ModalCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_eigen_two_storey_shear_frame.py b/tests/integration/test_eigen_two_storey_shear_frame.py index 724ba40..793f98d 100644 --- a/tests/integration/test_eigen_two_storey_shear_frame.py +++ b/tests/integration/test_eigen_two_storey_shear_frame.py @@ -8,9 +8,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import ModalCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import ModalCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_elastic_frame.py b/tests/integration/test_elastic_frame.py index 03aa14c..6f04163 100644 --- a/tests/integration/test_elastic_frame.py +++ b/tests/integration/test_elastic_frame.py @@ -8,9 +8,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import ModalCase, StaticCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import ModalCase, StaticCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -29,7 +29,6 @@ def _reload(proj, tmp_path): # type: ignore[no-untyped-def] def test_elastic_frame_gravity_reactions(tmp_path) -> None: # type: ignore[no-untyped-def] """ΣFy at base = total applied gravity (distributed w × beam × floors).""" from examples.elastic_frame import ( - BAY, LOAD_F1, LOAD_F2, LOAD_F3, @@ -70,7 +69,6 @@ def test_elastic_frame_gravity_reactions(tmp_path) -> None: # type: ignore[no-u def test_elastic_frame_gravity_plus_lateral_reactions(tmp_path) -> None: # type: ignore[no-untyped-def] """ΣFx at base must equal -(lateral applied) within PDelta tolerance.""" from examples.elastic_frame import ( - BAY, LOAD_F1, LOAD_F2, LOAD_F3, @@ -120,5 +118,5 @@ def test_elastic_frame_modal_periods(tmp_path) -> None: # type: ignore[no-untyp # 0.1930, 0.1562, 0.130 s. Our solve nails these within 1.5%. expected = [1.040, 0.3526, 0.1930, 0.1562, 0.130] periods = [2.0 * math.pi / math.sqrt(v) for v in r.eigenvalues] - for i, (T, T_ref) in enumerate(zip(periods, expected), start=1): - assert T == pytest.approx(T_ref, rel=0.02), f"T{i} = {T:.4f} s, reference {T_ref:.4f} s" + for i, (T, T_ref) in enumerate(zip(periods, expected, strict=False), start=1): + assert pytest.approx(T_ref, rel=0.02) == T, f"T{i} = {T:.4f} s, reference {T_ref:.4f} s" diff --git a/tests/integration/test_ex1a_canti2d.py b/tests/integration/test_ex1a_canti2d.py index e0d0a1d..a20d589 100644 --- a/tests/integration/test_ex1a_canti2d.py +++ b/tests/integration/test_ex1a_canti2d.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex1a_canti2d_eq.py b/tests/integration/test_ex1a_canti2d_eq.py index 69ad31c..264d30c 100644 --- a/tests/integration/test_ex1a_canti2d_eq.py +++ b/tests/integration/test_ex1a_canti2d_eq.py @@ -9,8 +9,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex1b_portal2d.py b/tests/integration/test_ex1b_portal2d.py index 324837e..f47bdd2 100644 --- a/tests/integration/test_ex1b_portal2d.py +++ b/tests/integration/test_ex1b_portal2d.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex2a_canti2d_elastic_element.py b/tests/integration/test_ex2a_canti2d_elastic_element.py index 3218683..71bfb6d 100644 --- a/tests/integration/test_ex2a_canti2d_elastic_element.py +++ b/tests/integration/test_ex2a_canti2d_elastic_element.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex2b_canti2d_inelastic_section.py b/tests/integration/test_ex2b_canti2d_inelastic_section.py index 2909e21..3766581 100644 --- a/tests/integration/test_ex2b_canti2d_inelastic_section.py +++ b/tests/integration/test_ex2b_canti2d_inelastic_section.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex2c_canti2d_inelastic_fiber_section.py b/tests/integration/test_ex2c_canti2d_inelastic_fiber_section.py index 86d6fa8..1cae060 100644 --- a/tests/integration/test_ex2c_canti2d_inelastic_fiber_section.py +++ b/tests/integration/test_ex2c_canti2d_inelastic_fiber_section.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex3_canti2d_variants.py b/tests/integration/test_ex3_canti2d_variants.py index 8007be3..2486519 100644 --- a/tests/integration/test_ex3_canti2d_variants.py +++ b/tests/integration/test_ex3_canti2d_variants.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_ex4_portal2d_variants.py b/tests/integration/test_ex4_portal2d_variants.py index e3abe5a..d4943cb 100644 --- a/tests/integration/test_ex4_portal2d_variants.py +++ b/tests/integration/test_ex4_portal2d_variants.py @@ -9,9 +9,9 @@ import pytest pytest.importorskip("openseespy") -from otko.core import PushoverCase, TransientCase # noqa: E402 -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.core import PushoverCase, TransientCase +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_moment_curvature.py b/tests/integration/test_moment_curvature.py index a73c4ec..12439d4 100644 --- a/tests/integration/test_moment_curvature.py +++ b/tests/integration/test_moment_curvature.py @@ -8,13 +8,11 @@ behaviour is exercised by the Phase 9 pushover tests. from __future__ import annotations -import math - import pytest pytest.importorskip("openseespy") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticUniaxial, FiberSection, LinearTimeSeries, @@ -26,7 +24,7 @@ from otko.core import ( # noqa: E402 StaticCase, ZeroLengthSectionElement, ) -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -134,7 +132,7 @@ def test_pushover_drives_rotation_for_moment_curvature() -> None: # Every (κ, M) point must satisfy M = E·I·κ (1 % tolerance allows # for the ~20-fibre discretisation of the rectangular section). - for kappa, moment in zip(result.control_disp, result.base_shear): + for kappa, moment in zip(result.control_disp, result.base_shear, strict=False): if abs(kappa) < 1e-12: continue expected_M = E * I * kappa diff --git a/tests/integration/test_moment_curvature_example.py b/tests/integration/test_moment_curvature_example.py index f0bed1f..7648e51 100644 --- a/tests/integration/test_moment_curvature_example.py +++ b/tests/integration/test_moment_curvature_example.py @@ -6,8 +6,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -15,13 +15,13 @@ pytestmark = pytest.mark.slow def test_moment_curvature_example_round_trips_and_converges(tmp_path) -> None: # type: ignore[no-untyped-def] """build_moment_curvature() → save → load → run → expected shape.""" from examples.moment_curvature import ( - build_moment_curvature, COL_DEPTH, COVER, E_STEEL, FY, MU, NUM_INCR, + build_moment_curvature, ) proj = build_moment_curvature() diff --git a/tests/integration/test_pattern_factors.py b/tests/integration/test_pattern_factors.py index 58d58a1..3c05c3e 100644 --- a/tests/integration/test_pattern_factors.py +++ b/tests/integration/test_pattern_factors.py @@ -8,7 +8,7 @@ import pytest ops = pytest.importorskip("openseespy.opensees") # skip if OpenSeesPy not installed -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, LinearTimeSeries, @@ -18,7 +18,7 @@ from otko.core import ( # noqa: E402 Project, StaticCase, ) -from otko.services import OpenSeesRunner # noqa: E402 +from otko.services import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_rc_frame_earthquake.py b/tests/integration/test_rc_frame_earthquake.py index 1d4d3dc..132535f 100644 --- a/tests/integration/test_rc_frame_earthquake.py +++ b/tests/integration/test_rc_frame_earthquake.py @@ -9,8 +9,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -18,9 +18,8 @@ pytestmark = pytest.mark.slow def test_rc_frame_earthquake_runs_and_has_oscillatory_response(tmp_path) -> None: # type: ignore[no-untyped-def] """Synthetic ground motion produces bounded, oscillatory response.""" from examples.rc_frame_earthquake import ( - build_rc_frame_earthquake, - DT, N_PTS, + build_rc_frame_earthquake, ) proj = build_rc_frame_earthquake() diff --git a/tests/integration/test_rc_frame_gravity.py b/tests/integration/test_rc_frame_gravity.py index 5250108..0c91190 100644 --- a/tests/integration/test_rc_frame_gravity.py +++ b/tests/integration/test_rc_frame_gravity.py @@ -6,8 +6,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -22,7 +22,7 @@ def test_rc_frame_gravity_matches_opensees_reference(tmp_path) -> None: # type: The column axial force is 180 kip compression (from the 180 kip load stepped onto each top node). """ - from examples.rc_frame_gravity import build_rc_frame_gravity, P_LOAD + from examples.rc_frame_gravity import P_LOAD, build_rc_frame_gravity proj = build_rc_frame_gravity() proj.validate_references() diff --git a/tests/integration/test_rc_frame_pushover.py b/tests/integration/test_rc_frame_pushover.py index 89518c3..5bd9ed0 100644 --- a/tests/integration/test_rc_frame_pushover.py +++ b/tests/integration/test_rc_frame_pushover.py @@ -6,8 +6,8 @@ import pytest pytest.importorskip("openseespy") -from otko.services import load_project, save_project # noqa: E402 -from otko.services.opensees_runner import OpenSeesRunner # noqa: E402 +from otko.services import load_project, save_project +from otko.services.opensees_runner import OpenSeesRunner pytestmark = pytest.mark.slow @@ -19,10 +19,11 @@ def test_rc_frame_pushover_reaches_target_with_fallback(tmp_path) -> None: # ty AND shows expected nonlinear shape. """ from examples.rc_frame_pushover import ( - build_rc_frame_pushover, D_STEP, D_TARGET, + build_rc_frame_pushover, ) + from otko.core import PushoverCase proj = build_rc_frame_pushover() diff --git a/tests/integration/test_runner_imposed_motion.py b/tests/integration/test_runner_imposed_motion.py index 54daa94..33c352b 100644 --- a/tests/integration/test_runner_imposed_motion.py +++ b/tests/integration/test_runner_imposed_motion.py @@ -23,7 +23,7 @@ import pytest ops = pytest.importorskip("openseespy.opensees") h5py = pytest.importorskip("h5py") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticUniaxial, ImposedSupportMotionPattern, Node, @@ -33,7 +33,7 @@ from otko.core import ( # noqa: E402 UniformExcitationPattern, ZeroLengthElement, ) -from otko.services import OpenSeesRunner # noqa: E402 +from otko.services import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_runner_modal.py b/tests/integration/test_runner_modal.py index 670e682..bf2a843 100644 --- a/tests/integration/test_runner_modal.py +++ b/tests/integration/test_runner_modal.py @@ -15,16 +15,16 @@ import math import pytest -ops = pytest.importorskip("openseespy.opensees") # noqa: F401 +ops = pytest.importorskip("openseespy.opensees") -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, ModalCase, Node, Project, ) -from otko.services import OpenSeesRunner # noqa: E402 +from otko.services import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_runner_static.py b/tests/integration/test_runner_static.py index 5722c0b..bd9cac1 100644 --- a/tests/integration/test_runner_static.py +++ b/tests/integration/test_runner_static.py @@ -12,7 +12,7 @@ import pytest ops = pytest.importorskip("openseespy.opensees") # skip if OpenSeesPy not installed -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, LinearTimeSeries, @@ -22,7 +22,7 @@ from otko.core import ( # noqa: E402 Project, StaticCase, ) -from otko.services import OpenSeesRunner # noqa: E402 +from otko.services import OpenSeesRunner pytestmark = pytest.mark.slow diff --git a/tests/integration/test_runner_transient.py b/tests/integration/test_runner_transient.py index 4715153..a2be365 100644 --- a/tests/integration/test_runner_transient.py +++ b/tests/integration/test_runner_transient.py @@ -12,10 +12,10 @@ import math import numpy as np import pytest -ops = pytest.importorskip("openseespy.opensees") # noqa: F401 -h5py = pytest.importorskip("h5py") # noqa: F401 +ops = pytest.importorskip("openseespy.opensees") +h5py = pytest.importorskip("h5py") -from otko.core import ( # noqa: E402 +from otko.core import ( ConstantTimeSeries, ElasticBeamColumn, ElasticSection, @@ -27,7 +27,7 @@ from otko.core import ( # noqa: E402 StaticCase, TransientCase, ) -from otko.services import OpenSeesRunner # noqa: E402 +from otko.services import OpenSeesRunner pytestmark = pytest.mark.slow @@ -51,7 +51,6 @@ def test_sdof_free_vibration_matches_cosine(tmp_path) -> None: # type: ignore[n # (already done), then verify dt-step Newmark integration of free # vibration starting from a static IC. F0 = 100.0 - u0 = F0 / k project = Project( ndm=2, diff --git a/tests/services/test_element_forces.py b/tests/services/test_element_forces.py index ad03cda..2f57387 100644 --- a/tests/services/test_element_forces.py +++ b/tests/services/test_element_forces.py @@ -19,7 +19,6 @@ from otko.services.element_forces import ( ) from otko.services.results import StaticResults - # ────────────────────────────────────────────────────────────────────── # Fixtures: a tiny 3D project with 2 elements + canned force results. # ────────────────────────────────────────────────────────────────────── diff --git a/tests/services/test_runner_translation.py b/tests/services/test_runner_translation.py index 1bbee24..1903183 100644 --- a/tests/services/test_runner_translation.py +++ b/tests/services/test_runner_translation.py @@ -23,7 +23,6 @@ from otko.core import ( Node, PlainLoadPattern, Project, - StaticCase, Steel01, TrussElement, ZeroLengthElement, diff --git a/tests/services/test_section_bbox.py b/tests/services/test_section_bbox.py index 43ab0c0..25f6b24 100644 --- a/tests/services/test_section_bbox.py +++ b/tests/services/test_section_bbox.py @@ -10,7 +10,6 @@ from otko.core import ( CircularPatch, ElasticSection, FiberSection, - Project, RectangularPatch, StraightLayer, ) diff --git a/tests/tools/test_parse_schemas.py b/tests/tools/test_parse_schemas.py index e1159aa..506496c 100644 --- a/tests/tools/test_parse_schemas.py +++ b/tests/tools/test_parse_schemas.py @@ -13,6 +13,7 @@ import json from pathlib import Path import pytest + from tools.gidopensees_import.parse_schemas import ( ParseError, _parse_dependencies, diff --git a/tests/unit/core/materials/test_concrete04.py b/tests/unit/core/materials/test_concrete04.py index 52142a0..5ea8311 100644 --- a/tests/unit/core/materials/test_concrete04.py +++ b/tests/unit/core/materials/test_concrete04.py @@ -136,6 +136,7 @@ class TestConcrete04JsonRoundTrip: def test_old_osmodel_without_concrete04_loads_cleanly(self) -> None: """An osmodel payload that doesn't mention Concrete04 is unaffected.""" from pathlib import Path + from otko.services import load_project osmodel = Path(__file__).parents[4] / "examples" / "cantilever.osmodel" diff --git a/tests/unit/test_diagram_renderer.py b/tests/unit/test_diagram_renderer.py index cf6ae38..425606b 100644 --- a/tests/unit/test_diagram_renderer.py +++ b/tests/unit/test_diagram_renderer.py @@ -15,21 +15,21 @@ import pytest # Force pyvista off-screen before any pyvista import in this module's chain. os.environ.setdefault("PYVISTA_OFF_SCREEN", "true") -import pyvista as pv # noqa: E402 +import pyvista as pv -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, Node, Project, ) -from otko.services.element_forces import ( # noqa: E402 +from otko.services.element_forces import ( DiagramData, ForceComponent, extract_diagram_data, ) -from otko.services.results import StaticResults # noqa: E402 -from otko.views.canvas3d.diagram_renderer import DiagramRenderer # noqa: E402 +from otko.services.results import StaticResults +from otko.views.canvas3d.diagram_renderer import DiagramRenderer pv.OFF_SCREEN = True diff --git a/tests/unit/test_elements.py b/tests/unit/test_elements.py index 11987c6..e3c971a 100644 --- a/tests/unit/test_elements.py +++ b/tests/unit/test_elements.py @@ -6,8 +6,8 @@ import pytest from pydantic import TypeAdapter, ValidationError from otko.core import ( - Element, ElasticBeamColumn, + Element, ForceBeamColumn, TrussElement, ZeroLengthElement, diff --git a/tests/unit/test_grid_system.py b/tests/unit/test_grid_system.py index adcbb55..6688dce 100644 --- a/tests/unit/test_grid_system.py +++ b/tests/unit/test_grid_system.py @@ -2,7 +2,6 @@ from __future__ import annotations -import math from pathlib import Path import pytest @@ -174,7 +173,7 @@ def test_coord_system_round_trip_world_local() -> None: p_local = (1.5, -0.5, 2.0) p_world = cs.local_to_world(p_local) p_back = cs.world_to_local(p_world) - for a, b in zip(p_local, p_back): + for a, b in zip(p_local, p_back, strict=False): assert a == pytest.approx(b, abs=1e-9) diff --git a/tests/unit/test_phase8a.py b/tests/unit/test_phase8a.py index 0e67780..c9a3842 100644 --- a/tests/unit/test_phase8a.py +++ b/tests/unit/test_phase8a.py @@ -8,7 +8,6 @@ from __future__ import annotations -import numpy as np import pytest from otko.commands.nodes import SetMassCommand diff --git a/tests/unit/test_phase9.py b/tests/unit/test_phase9.py index 04af175..24b38b9 100644 --- a/tests/unit/test_phase9.py +++ b/tests/unit/test_phase9.py @@ -10,9 +10,7 @@ import pytest from otko.core import ( AggregatorDOF, CircularPatch, - ElasticSection, FiberSection, - Fibre, Project, RectangularPatch, SectionAggregator, @@ -155,7 +153,7 @@ def test_Iz_of_rect_matches_analytical() -> None: # ── Round-trip persistence ─────────────────────────────────────────── def test_fiber_section_with_patches_round_trips(tmp_path) -> None: # type: ignore[no-untyped-def] - from otko.core import Node, ElasticBeamColumn + from otko.core import ElasticBeamColumn, Node p = Project( nodes=[Node(id=1, coords=(0, 0, 0)), Node(id=2, coords=(1, 0, 0))], diff --git a/tests/unit/test_renderer.py b/tests/unit/test_renderer.py index e1c8fb0..1586ca2 100644 --- a/tests/unit/test_renderer.py +++ b/tests/unit/test_renderer.py @@ -5,9 +5,9 @@ from __future__ import annotations import pytest pv = pytest.importorskip("pyvista") -import numpy as np # noqa: E402 +import numpy as np -from otko.core import ( # noqa: E402 +from otko.core import ( ElasticBeamColumn, ElasticSection, LinearTimeSeries, @@ -18,8 +18,8 @@ from otko.core import ( # noqa: E402 Steel01, TrussElement, ) -from otko.services.deformation import DeformationSource # noqa: E402 -from otko.views.canvas3d.model_renderer import ( # noqa: E402 +from otko.services.deformation import DeformationSource +from otko.views.canvas3d.model_renderer import ( ModelRenderer, RendererMode, _classify_support, diff --git a/tests/unit/test_unit_labels.py b/tests/unit/test_unit_labels.py index 1fe1e3e..999f0e4 100644 --- a/tests/unit/test_unit_labels.py +++ b/tests/unit/test_unit_labels.py @@ -2,8 +2,6 @@ from __future__ import annotations -import pytest - from otko.core import UnitSystem, labels_for diff --git a/tests/unit/test_zerolength_section.py b/tests/unit/test_zerolength_section.py index 696b83e..69ef334 100644 --- a/tests/unit/test_zerolength_section.py +++ b/tests/unit/test_zerolength_section.py @@ -3,6 +3,7 @@ from __future__ import annotations import pytest +from pydantic import ValidationError from otko.core import ( ElasticSection, @@ -21,7 +22,7 @@ def test_zero_length_section_schema_defaults() -> None: def test_zero_length_section_rejects_extra_fields() -> None: - with pytest.raises(Exception): + with pytest.raises(ValidationError): ZeroLengthSectionElement( id=1, nodes=(1, 2),