sarj-python-lint 1.28.1__tar.gz → 1.28.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/PKG-INFO +2 -2
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/pyproject.toml +2 -2
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_file_context.py +4 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_resource_provenance.py +10 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +23 -3
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +4 -3
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +8 -6
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +1 -8
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +1 -10
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +1 -9
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +1 -9
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +4 -1
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +2 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/unused_mock_setup.py +159 -78
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/.gitignore +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/LICENSE +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/README.md +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.28.1 → sarj_python_lint-1.28.3}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 1.28.
|
|
3
|
+
Version: 1.28.3
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
|
|
6
6
|
Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
|
|
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
|
|
|
19
19
|
Requires-Python: >=3.14
|
|
20
20
|
Requires-Dist: packaging==26.3
|
|
21
21
|
Requires-Dist: ruff>=0.16.10
|
|
22
|
-
Requires-Dist: sarj-rule-contracts==0.2.
|
|
22
|
+
Requires-Dist: sarj-rule-contracts==0.2.3
|
|
23
23
|
Requires-Dist: sqlglot==30.19.0
|
|
24
24
|
Requires-Dist: typer==0.27.2
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sarj-python-lint"
|
|
3
|
-
version = "1.28.
|
|
3
|
+
version = "1.28.3"
|
|
4
4
|
description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "sarj-ai" }]
|
|
@@ -15,7 +15,7 @@ classifiers = [
|
|
|
15
15
|
"Programming Language :: Python :: 3.14",
|
|
16
16
|
"Topic :: Software Development :: Quality Assurance",
|
|
17
17
|
]
|
|
18
|
-
dependencies = ["sarj-rule-contracts==0.2.
|
|
18
|
+
dependencies = ["sarj-rule-contracts==0.2.3", "packaging==26.3", "ruff>=0.16.10", "sqlglot==30.19.0", "typer==0.27.2"]
|
|
19
19
|
|
|
20
20
|
[project.scripts]
|
|
21
21
|
sarj-python-lint = "sarj_python_lint.__main__:main"
|
|
@@ -57,6 +57,10 @@ class PythonFileContext:
|
|
|
57
57
|
def nodes[NodeT: ast.AST](self, *types: type[NodeT]) -> list[NodeT]:
|
|
58
58
|
return [node for node in self.node_index.query(types) if isinstance(node, types)]
|
|
59
59
|
|
|
60
|
+
@cached_property
|
|
61
|
+
def has_wildcard_import(self) -> bool:
|
|
62
|
+
return any(alias.name == "*" for node in self.nodes(ast.ImportFrom) for alias in node.names)
|
|
63
|
+
|
|
60
64
|
@property
|
|
61
65
|
def parents(self) -> Mapping[ast.AST, ast.AST]:
|
|
62
66
|
return self.node_index.parents
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
from collections import defaultdict
|
|
5
|
+
from functools import cached_property
|
|
5
6
|
from typing import TYPE_CHECKING, final
|
|
6
7
|
|
|
7
8
|
|
|
@@ -44,6 +45,15 @@ class ResourceProvenance:
|
|
|
44
45
|
if root is not None:
|
|
45
46
|
self.mutated.add(root)
|
|
46
47
|
|
|
48
|
+
@cached_property
|
|
49
|
+
def builtin_namespace_untouched(self) -> bool:
|
|
50
|
+
return not any(
|
|
51
|
+
(isinstance(node, ast.Import) and any(alias.name == "builtins" for alias in node.names))
|
|
52
|
+
or (isinstance(node, ast.ImportFrom) and node.module == "builtins")
|
|
53
|
+
or (isinstance(node, ast.Name) and node.id == "__builtins__")
|
|
54
|
+
for node in self.context.nodes(ast.Import, ast.ImportFrom, ast.Name)
|
|
55
|
+
)
|
|
56
|
+
|
|
47
57
|
def scope(self, node: ast.AST) -> ast.AST | None:
|
|
48
58
|
parent = self.context.parents.get(node)
|
|
49
59
|
while parent is not None and not isinstance(parent, _SCOPES):
|
|
@@ -59,8 +59,8 @@ class DuplicateAdjacentExceptionHandler(ProjectRule):
|
|
|
59
59
|
autofix=AutofixPolicy.NONE,
|
|
60
60
|
limitations=(
|
|
61
61
|
"Only ordinary try statements with adjacent handlers, identical exception bindings, executable ASTs and comments are compared; exception groups and bare re-raise/pass handlers are excluded.",
|
|
62
|
-
"Handler types must be simple names or tuples of unshadowed builtin or statically resolved first-party Exception subclasses, with
|
|
63
|
-
"Dynamic namespace access, unknown external exception classes, generated/vendor sources, different comments, and intervening handlers are excluded. No autofix preserves intentional translation seams.",
|
|
62
|
+
"Handler types must be simple names or tuples of unshadowed builtin or statically resolved first-party Exception subclasses, with stable declarations/imports initialized before the enclosing module statement. Declarations after a callable's definition are unresolved and excluded.",
|
|
63
|
+
"Dynamic or explicit builtin namespace access, unknown external exception classes, generated/vendor sources, different comments, and intervening handlers are excluded. Unseen external builtin mutations are not resolved. No autofix preserves intentional translation seams.",
|
|
64
64
|
),
|
|
65
65
|
examples=tuple(
|
|
66
66
|
RuleExample(
|
|
@@ -88,6 +88,8 @@ class DuplicateAdjacentExceptionHandler(ProjectRule):
|
|
|
88
88
|
project = context.session.project or ProjectIndexSet.single(context.path, context.source)
|
|
89
89
|
unit = project.unit_or_source(context.path, context.source, tree)
|
|
90
90
|
provenance = ResourceProvenance(context)
|
|
91
|
+
if not provenance.builtin_namespace_untouched:
|
|
92
|
+
return []
|
|
91
93
|
comments = scan_comments(context.source)
|
|
92
94
|
findings: list[Diagnostic] = []
|
|
93
95
|
for statement in context.nodes(ast.Try):
|
|
@@ -176,7 +178,11 @@ def _first_party_exception(
|
|
|
176
178
|
from sarj_python_lint._file_context import PythonFileContext # ruff: ignore[import-outside-top-level] -- defer until the rule registry finishes loading.
|
|
177
179
|
|
|
178
180
|
binding = provenance.binding(expression.id, expression)
|
|
179
|
-
if
|
|
181
|
+
if (
|
|
182
|
+
not isinstance(binding, ast.ClassDef | ast.alias)
|
|
183
|
+
or unit is None
|
|
184
|
+
or not _initialized_before(binding, expression, context)
|
|
185
|
+
):
|
|
180
186
|
return False
|
|
181
187
|
if isinstance(binding, ast.alias):
|
|
182
188
|
statement = context.parents.get(binding)
|
|
@@ -200,6 +206,7 @@ def _first_party_exception(
|
|
|
200
206
|
)
|
|
201
207
|
return (
|
|
202
208
|
declaration is not None
|
|
209
|
+
and definition_provenance.builtin_namespace_untouched
|
|
203
210
|
and stable_class(definition_context, declaration, definition_provenance)
|
|
204
211
|
and len(declaration.bases) == 1
|
|
205
212
|
and _safe_types(
|
|
@@ -211,3 +218,16 @@ def _first_party_exception(
|
|
|
211
218
|
seen=seen | {summary.symbol},
|
|
212
219
|
)
|
|
213
220
|
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _initialized_before(binding: ast.ClassDef | ast.alias, read: ast.Name, context: PythonFileContext) -> bool:
|
|
224
|
+
declaration = context.parents.get(binding) if isinstance(binding, ast.alias) else binding
|
|
225
|
+
boundary: ast.AST = read
|
|
226
|
+
while (parent := context.parents.get(boundary)) is not None and parent is not context.tree:
|
|
227
|
+
boundary = parent
|
|
228
|
+
return (
|
|
229
|
+
isinstance(declaration, ast.stmt)
|
|
230
|
+
and context.parents.get(declaration) is context.tree
|
|
231
|
+
and isinstance(boundary, ast.stmt)
|
|
232
|
+
and (declaration.end_lineno or declaration.lineno) < boundary.lineno
|
|
233
|
+
)
|
|
@@ -131,11 +131,12 @@ def _pytest_plugin_test_targets(tree: ast.Module) -> list[_PluginTarget]:
|
|
|
131
131
|
for statement in tree.body:
|
|
132
132
|
value: ast.expr | None = None
|
|
133
133
|
match statement:
|
|
134
|
-
case (
|
|
135
|
-
|
|
136
|
-
| ast.AnnAssign(target=ast.Name(id="pytest_plugins"))
|
|
134
|
+
case ast.Assign(targets=targets) if any(
|
|
135
|
+
isinstance(target, ast.Name) and target.id == "pytest_plugins" for target in targets
|
|
137
136
|
):
|
|
138
137
|
value = statement.value
|
|
138
|
+
case ast.AnnAssign(target=ast.Name(id="pytest_plugins")):
|
|
139
|
+
value = statement.value
|
|
139
140
|
case _:
|
|
140
141
|
continue
|
|
141
142
|
findings.extend(
|
|
@@ -57,10 +57,10 @@ class OpaqueParametrizeCaseNeedsId(Rule):
|
|
|
57
57
|
code: str = "SARJ042"
|
|
58
58
|
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
59
59
|
default_level=Severity.WARNING,
|
|
60
|
-
summary="Opaque static pytest parameter cases
|
|
61
|
-
rationale="
|
|
60
|
+
summary="Opaque static pytest parameter cases lack table-local semantic IDs.",
|
|
61
|
+
rationale="Without a semantic naming owner, fallback IDs such as `payload0` are hard to diagnose and silently change when the table is reordered.",
|
|
62
62
|
remediation=(
|
|
63
|
-
"
|
|
63
|
+
"Check the owning pytest_make_parametrize_id hook before adding IDs; reuse an existing semantic naming owner. Otherwise give each opaque row a semantic `pytest.param(..., id=...)`, or use a semantic callable `ids=` when one "
|
|
64
64
|
"mapping applies to the entire table."
|
|
65
65
|
),
|
|
66
66
|
category=RuleCategory.TESTING,
|
|
@@ -129,10 +129,12 @@ class OpaqueParametrizeCaseNeedsId(Rule):
|
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
def _message(count: int) -> str:
|
|
132
|
-
subject = "case
|
|
132
|
+
subject = "case lacks" if count == 1 else "cases lack"
|
|
133
133
|
return (
|
|
134
|
-
f"{count} {subject}
|
|
135
|
-
'
|
|
134
|
+
f"{count} {subject} a table-local semantic ID. Check the owning `pytest_make_parametrize_id` hook before "
|
|
135
|
+
'adding IDs; reuse an existing semantic naming owner, or add semantic `pytest.param(..., id="...")` values '
|
|
136
|
+
"or a semantic callable `ids=`. Without a semantic ID, pytest may use an argument-name-and-index fallback "
|
|
137
|
+
"(for example, `payload0`), which renumbers when rows are reordered."
|
|
136
138
|
)
|
|
137
139
|
|
|
138
140
|
|
|
@@ -177,7 +177,7 @@ class PreferMatchExceptionDispatch(Rule):
|
|
|
177
177
|
imports = context.imports
|
|
178
178
|
if (
|
|
179
179
|
not imports.builtin_is_unshadowed("isinstance")
|
|
180
|
-
or
|
|
180
|
+
or context.has_wildcard_import
|
|
181
181
|
or _has_local_import(tree, node_index=context.node_index)
|
|
182
182
|
):
|
|
183
183
|
return []
|
|
@@ -412,13 +412,6 @@ def _body_terminates(body: list[ast.stmt]) -> bool:
|
|
|
412
412
|
return False
|
|
413
413
|
|
|
414
414
|
|
|
415
|
-
def _has_wildcard_import(tree: ast.Module, *, node_index: NodeIndex | None = None) -> bool:
|
|
416
|
-
return any(
|
|
417
|
-
isinstance(node, ast.ImportFrom) and any(alias.name == "*" for alias in node.names)
|
|
418
|
-
for node in walk_ast(tree, index=node_index)
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
|
|
422
415
|
def _has_local_import(tree: ast.Module, *, node_index: NodeIndex | None = None) -> bool:
|
|
423
416
|
module_imports = {id(statement) for statement in tree.body if isinstance(statement, (ast.Import, ast.ImportFrom))}
|
|
424
417
|
return any(
|
|
@@ -16,12 +16,10 @@ from sarj_python_lint.rule_base import (
|
|
|
16
16
|
Severity,
|
|
17
17
|
parse_or_none,
|
|
18
18
|
)
|
|
19
|
-
from sarj_python_lint.rules._ast_index import walk as walk_ast
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
if TYPE_CHECKING:
|
|
23
22
|
from sarj_python_lint._file_context import PythonFileContext
|
|
24
|
-
from sarj_python_lint.rules._ast_index import NodeIndex
|
|
25
23
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
26
24
|
|
|
27
25
|
|
|
@@ -101,7 +99,7 @@ class PreferNamedtupleOverTupleReturn(Rule):
|
|
|
101
99
|
if context.generated or _is_documentation_path(path):
|
|
102
100
|
return []
|
|
103
101
|
tree = context.tree
|
|
104
|
-
if tree is None or
|
|
102
|
+
if tree is None or context.has_wildcard_import:
|
|
105
103
|
return []
|
|
106
104
|
imports = context.imports
|
|
107
105
|
diagnostics: list[Diagnostic] = []
|
|
@@ -127,13 +125,6 @@ def _is_documentation_path(path: Path) -> bool:
|
|
|
127
125
|
return any(part.lower() in _DOCUMENTATION_DIR_NAMES for part in path.parts)
|
|
128
126
|
|
|
129
127
|
|
|
130
|
-
def _has_wildcard_import(tree: ast.Module, *, node_index: NodeIndex | None = None) -> bool:
|
|
131
|
-
return any(
|
|
132
|
-
isinstance(node, ast.ImportFrom) and any(alias.name == "*" for alias in node.names)
|
|
133
|
-
for node in walk_ast(tree, index=node_index)
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
137
128
|
def _is_public_record_tuple(annotation: ast.expr, imports: ImportIndex) -> bool:
|
|
138
129
|
annotation = _unwrap_return_annotation(annotation, imports)
|
|
139
130
|
if not isinstance(annotation, ast.Subscript) or not isinstance(annotation.slice, ast.Tuple):
|
|
@@ -27,7 +27,6 @@ if TYPE_CHECKING:
|
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
29
|
from sarj_python_lint._file_context import PythonFileContext
|
|
30
|
-
from sarj_python_lint.rules._ast_index import NodeIndex
|
|
31
30
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
32
31
|
|
|
33
32
|
|
|
@@ -221,7 +220,7 @@ class PreferNominalIdTypes(Rule):
|
|
|
221
220
|
if _is_excluded_path(path) or context.generated:
|
|
222
221
|
return []
|
|
223
222
|
tree = context.tree
|
|
224
|
-
if tree is None or
|
|
223
|
+
if tree is None or context.has_wildcard_import:
|
|
225
224
|
return []
|
|
226
225
|
|
|
227
226
|
imports = context.imports
|
|
@@ -311,13 +310,6 @@ def _is_excluded_path(path: Path) -> bool:
|
|
|
311
310
|
)
|
|
312
311
|
|
|
313
312
|
|
|
314
|
-
def _has_wildcard_import(tree: ast.Module, *, node_index: NodeIndex | None = None) -> bool:
|
|
315
|
-
return any(
|
|
316
|
-
isinstance(node, ast.ImportFrom) and any(alias.name == "*" for alias in node.names)
|
|
317
|
-
for node in walk_ast(tree, index=node_index)
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
|
|
321
313
|
def _boundary_nodes(
|
|
322
314
|
tree: ast.Module,
|
|
323
315
|
imports: ImportIndex,
|
|
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
|
|
|
23
23
|
from collections.abc import Iterator
|
|
24
24
|
|
|
25
25
|
from sarj_python_lint._file_context import PythonFileContext
|
|
26
|
-
from sarj_python_lint.rules._ast_index import NodeIndex
|
|
27
26
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
28
27
|
|
|
29
28
|
|
|
@@ -99,7 +98,7 @@ class PreferNonNullableCollection(Rule):
|
|
|
99
98
|
if is_test_path(path) or is_test_support_path(path) or context.generated:
|
|
100
99
|
return []
|
|
101
100
|
tree = context.tree
|
|
102
|
-
if tree is None or
|
|
101
|
+
if tree is None or context.has_wildcard_import:
|
|
103
102
|
return []
|
|
104
103
|
|
|
105
104
|
imports = context.imports
|
|
@@ -125,13 +124,6 @@ class PreferNonNullableCollection(Rule):
|
|
|
125
124
|
return sorted(diagnostics, key=lambda diagnostic: (diagnostic.line, diagnostic.col))
|
|
126
125
|
|
|
127
126
|
|
|
128
|
-
def _has_wildcard_import(tree: ast.Module, *, node_index: NodeIndex | None = None) -> bool:
|
|
129
|
-
return any(
|
|
130
|
-
isinstance(node, ast.ImportFrom) and any(alias.name == "*" for alias in node.names)
|
|
131
|
-
for node in walk_ast(tree, index=node_index)
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
|
|
135
127
|
def _eligible_functions(tree: ast.Module, imports: ImportIndex) -> list[ast.FunctionDef | ast.AsyncFunctionDef]:
|
|
136
128
|
functions: list[ast.FunctionDef | ast.AsyncFunctionDef] = []
|
|
137
129
|
for statement in tree.body:
|
|
@@ -61,7 +61,10 @@ class RedundantTestConstructorForwarder(ProjectRule):
|
|
|
61
61
|
example_id=name,
|
|
62
62
|
title=title,
|
|
63
63
|
outcome=outcome,
|
|
64
|
-
files=(
|
|
64
|
+
files=(
|
|
65
|
+
ExampleFile.python("tests/__init__.py", "# Example test package.\n"),
|
|
66
|
+
ExampleFile.python("tests/test_record.py", source),
|
|
67
|
+
),
|
|
65
68
|
focus_path=PurePosixPath("tests/test_record.py"),
|
|
66
69
|
expected_count=count,
|
|
67
70
|
public=True,
|
|
@@ -60,6 +60,7 @@ class RepeatedUnimplementedTestContract(ProjectRule):
|
|
|
60
60
|
title="Two test fakes repeat one unconfigured operation",
|
|
61
61
|
outcome=ExampleOutcome.MATCH,
|
|
62
62
|
files=(
|
|
63
|
+
ExampleFile.python("tests/__init__.py", "# Example test package.\n"),
|
|
63
64
|
ExampleFile.python(
|
|
64
65
|
"tests/test_store.py",
|
|
65
66
|
"from abc import ABC, abstractmethod\nclass Store(ABC):\n @abstractmethod\n def read(self, key: str) -> str:\n pass\nclass First(Store):\n def read(self, key: str) -> str:\n raise NotImplementedError\nclass Second(Store):\n def read(self, key: str) -> str:\n raise NotImplementedError\n",
|
|
@@ -74,6 +75,7 @@ class RepeatedUnimplementedTestContract(ProjectRule):
|
|
|
74
75
|
title="Configured fake behavior belongs to the leaf",
|
|
75
76
|
outcome=ExampleOutcome.NO_MATCH,
|
|
76
77
|
files=(
|
|
78
|
+
ExampleFile.python("tests/__init__.py", "# Example test package.\n"),
|
|
77
79
|
ExampleFile.python(
|
|
78
80
|
"tests/test_store.py",
|
|
79
81
|
"from abc import ABC, abstractmethod\nclass Store(ABC):\n @abstractmethod\n def read(self, key: str) -> str:\n pass\nclass First(Store):\n def read(self, key: str) -> str:\n raise NotImplementedError\nclass Second(Store):\n def read(self, key: str) -> str:\n return key\n",
|