sarj-python-lint 1.36.0__tar.gz → 1.37.0__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.36.0 → sarj_python_lint-1.37.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/pyproject.toml +1 -1
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rule_base.py +1 -0
- sarj_python_lint-1.37.0/src/sarj_python_lint/rules/__init__.py +3 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_project_index.py +20 -17
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_registry.py +7 -0
- sarj_python_lint-1.37.0/src/sarj_python_lint/rules/test_without_explicit_oracle.py +244 -0
- sarj_python_lint-1.36.0/src/sarj_python_lint/rules/__init__.py +0 -3
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/.gitignore +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/LICENSE +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/README.md +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/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.
|
|
3
|
+
Version: 1.37.0
|
|
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/
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
@@ -566,20 +566,22 @@ class ProjectIndexSet:
|
|
|
566
566
|
if isinstance(owner, ast.ClassDef) and _class_has_typed_dependency(candidate, owner, target)
|
|
567
567
|
)
|
|
568
568
|
|
|
569
|
+
@cached_property
|
|
570
|
+
def _mock_specs(self) -> Mapping[SymbolRef, frozenset[Path]]:
|
|
571
|
+
specs: dict[SymbolRef, set[Path]] = {}
|
|
572
|
+
for candidate in self._units.values():
|
|
573
|
+
if candidate.tree is None:
|
|
574
|
+
continue
|
|
575
|
+
for call in walk_ast(candidate.tree):
|
|
576
|
+
if isinstance(call, ast.Call):
|
|
577
|
+
for target in _mock_spec_targets(candidate, call):
|
|
578
|
+
specs.setdefault(target, set()).add(candidate.path)
|
|
579
|
+
return MappingProxyType({target: frozenset(paths) for target, paths in specs.items()})
|
|
580
|
+
|
|
569
581
|
def test_mock_specs(self, unit: SourceUnit, name: str) -> frozenset[Path]:
|
|
570
582
|
if unit.module is None:
|
|
571
583
|
return frozenset()
|
|
572
|
-
|
|
573
|
-
return frozenset(
|
|
574
|
-
candidate.path
|
|
575
|
-
for candidate in self._units.values()
|
|
576
|
-
if candidate.tree is not None
|
|
577
|
-
and any(
|
|
578
|
-
_is_mock_spec_for(candidate, call, target)
|
|
579
|
-
for call in walk_ast(candidate.tree)
|
|
580
|
-
if isinstance(call, ast.Call)
|
|
581
|
-
)
|
|
582
|
-
)
|
|
584
|
+
return self._mock_specs.get(SymbolRef(unit.module, name), frozenset())
|
|
583
585
|
|
|
584
586
|
def class_inherits_from(self, unit: SourceUnit, name: str, qualified_bases: frozenset[str]) -> bool:
|
|
585
587
|
if unit.module is None:
|
|
@@ -1350,14 +1352,15 @@ def _class_has_typed_dependency(unit: SourceUnit, owner: ast.ClassDef, target: S
|
|
|
1350
1352
|
return False
|
|
1351
1353
|
|
|
1352
1354
|
|
|
1353
|
-
def
|
|
1355
|
+
def _mock_spec_targets(unit: SourceUnit, call: ast.Call) -> Iterator[SymbolRef]:
|
|
1354
1356
|
mock_name = _mock_function_name(unit, call.func)
|
|
1355
1357
|
if mock_name is None:
|
|
1356
|
-
return
|
|
1357
|
-
|
|
1358
|
-
keyword.arg in {"spec", "spec_set"} and _direct_base_symbol(unit, keyword.value)
|
|
1359
|
-
|
|
1360
|
-
|
|
1358
|
+
return
|
|
1359
|
+
for keyword in call.keywords:
|
|
1360
|
+
if keyword.arg in {"spec", "spec_set"} and (target := _direct_base_symbol(unit, keyword.value)) is not None:
|
|
1361
|
+
yield target
|
|
1362
|
+
if mock_name == "create_autospec" and call.args and (target := _direct_base_symbol(unit, call.args[0])) is not None:
|
|
1363
|
+
yield target
|
|
1361
1364
|
|
|
1362
1365
|
|
|
1363
1366
|
def _mock_function_name(unit: SourceUnit, function: ast.expr) -> str | None:
|
|
@@ -215,10 +215,12 @@ from sarj_python_lint.rules.no_impossible_pydantic_field_bounds import NoImpossi
|
|
|
215
215
|
from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
|
|
216
216
|
from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
|
|
217
217
|
from sarj_python_lint.rules.repeated_unimplemented_test_contract import RepeatedUnimplementedTestContract
|
|
218
|
+
from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOracle
|
|
218
219
|
|
|
219
220
|
|
|
220
221
|
REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
221
222
|
{
|
|
223
|
+
NoExplicitTestOracle.id: NoExplicitTestOracle,
|
|
222
224
|
NoConflictingPydanticAliasValidation.id: NoConflictingPydanticAliasValidation,
|
|
223
225
|
NoUnknownPydanticValidatorField.id: NoUnknownPydanticValidatorField,
|
|
224
226
|
NoImpossiblePydanticFieldBounds.id: NoImpossiblePydanticFieldBounds,
|
|
@@ -367,3 +369,8 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
367
369
|
RepeatedUnimplementedTestContract.id: RepeatedUnimplementedTestContract,
|
|
368
370
|
}
|
|
369
371
|
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
DEFAULT_REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
375
|
+
{name: rule for name, rule in REGISTRY.items() if rule.enabled_by_default}
|
|
376
|
+
)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
from typing import TYPE_CHECKING, ClassVar, final, override
|
|
6
|
+
|
|
7
|
+
from sarj_python_lint.rule_base import (
|
|
8
|
+
AutofixPolicy,
|
|
9
|
+
Diagnostic,
|
|
10
|
+
ExampleFile,
|
|
11
|
+
ExampleOutcome,
|
|
12
|
+
Rule,
|
|
13
|
+
RuleCategory,
|
|
14
|
+
RuleDocumentation,
|
|
15
|
+
RuleExample,
|
|
16
|
+
Severity,
|
|
17
|
+
)
|
|
18
|
+
from sarj_python_lint.rules._paths import is_test_path
|
|
19
|
+
from sarj_python_lint.rules._pytest import uses_benchmark_fixture
|
|
20
|
+
from sarj_python_lint.rules._test_assertions import names_verification, reads_as_verification
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Iterator, Sequence
|
|
25
|
+
|
|
26
|
+
from sarj_python_lint._file_context import PythonFileContext
|
|
27
|
+
from sarj_python_lint.rules._imports import ImportIndex
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
type _Function = ast.FunctionDef | ast.AsyncFunctionDef
|
|
31
|
+
_NON_UNIT_MARKS = frozenset({"benchmark", "lto", "lto_judge", "skip", "skipif", "xfail"})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@final
|
|
35
|
+
class NoExplicitTestOracle(Rule):
|
|
36
|
+
id = "test-without-explicit-oracle"
|
|
37
|
+
code = "SARJ495"
|
|
38
|
+
enabled_by_default = False
|
|
39
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
40
|
+
default_level=Severity.WARNING,
|
|
41
|
+
summary="A collected test has no visible assertion or exception oracle.",
|
|
42
|
+
rationale=(
|
|
43
|
+
"A test can execute a side effect without observing whether the intended effect occurred. "
|
|
44
|
+
"The absence of an explicit oracle is a review candidate, not proof that a smoke or no-throw contract is weak."
|
|
45
|
+
),
|
|
46
|
+
remediation=(
|
|
47
|
+
"Identify the intended contract. Reuse adequate existing coverage, observe the returned value, state, "
|
|
48
|
+
"or emitted effect, or retain the test when successful execution is itself the contract. Do not add "
|
|
49
|
+
"a tautological assertion or delete a test solely to silence this advisory."
|
|
50
|
+
),
|
|
51
|
+
category=RuleCategory.TESTING,
|
|
52
|
+
autofix=AutofixPolicy.NONE,
|
|
53
|
+
limitations=(
|
|
54
|
+
"This opt-in advisory is excluded from default Python checks; select it explicitly or use code-standards observe.",
|
|
55
|
+
"Only default-collected test_* functions and methods of undecorated or pytest-marked, constructor-free, base-free Test* classes are inspected.",
|
|
56
|
+
"Assertions, explicit raises, verification-named calls, import aliases, and local helpers containing or forwarding an oracle are accepted conservatively.",
|
|
57
|
+
"Unknown decorators, test opt-outs, benchmark/LLM/skipped tests, and callable fixture parameters are left unreported; external helper behavior is not inferred.",
|
|
58
|
+
"An intentional no-throw test may match. Findings describe syntax and require contract review; they never authorize automatic deletion or error-level promotion.",
|
|
59
|
+
),
|
|
60
|
+
examples=(
|
|
61
|
+
RuleExample(
|
|
62
|
+
example_id="unobserved-effect",
|
|
63
|
+
title="Observe whether the requested side effect occurred",
|
|
64
|
+
outcome=ExampleOutcome.MATCH,
|
|
65
|
+
files=(ExampleFile.python("tests/test_delivery.py", "def test_delivery():\n deliver('hello')\n"),),
|
|
66
|
+
focus_path=PurePosixPath("tests/test_delivery.py"),
|
|
67
|
+
expected_count=1,
|
|
68
|
+
public=True,
|
|
69
|
+
),
|
|
70
|
+
RuleExample(
|
|
71
|
+
example_id="observed-effect",
|
|
72
|
+
title="The assertion distinguishes a missing delivery",
|
|
73
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
74
|
+
files=(
|
|
75
|
+
ExampleFile.python(
|
|
76
|
+
"tests/test_delivery.py",
|
|
77
|
+
"def test_delivery(sender):\n deliver('hello', sender)\n assert sender.messages == ['hello']\n",
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
focus_path=PurePosixPath("tests/test_delivery.py"),
|
|
81
|
+
expected_count=0,
|
|
82
|
+
public=True,
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
description = documentation.summary
|
|
87
|
+
|
|
88
|
+
@override
|
|
89
|
+
def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
|
|
90
|
+
if not is_test_path(context.path) or context.path.name == "conftest.py" or context.generated:
|
|
91
|
+
return []
|
|
92
|
+
tree = context.tree
|
|
93
|
+
if tree is None or _opts_out(tree.body) or not _scope_marks(tree.body, context.imports):
|
|
94
|
+
return []
|
|
95
|
+
helpers = _oracle_helpers(tree, context.imports)
|
|
96
|
+
return [
|
|
97
|
+
Diagnostic(
|
|
98
|
+
path=context.path,
|
|
99
|
+
line=function.lineno,
|
|
100
|
+
col=function.col_offset + 1,
|
|
101
|
+
code=self.code,
|
|
102
|
+
severity=Severity.WARNING,
|
|
103
|
+
message=(
|
|
104
|
+
f"`{function.name}` has no visible assertion or exception oracle. Review the contract: "
|
|
105
|
+
"observe the outcome, reuse existing coverage, or retain an intentional smoke/no-throw test."
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
for function in _collected_tests(tree, context.imports)
|
|
109
|
+
if not uses_benchmark_fixture(function)
|
|
110
|
+
and not _has_oracle(function, helpers, context.imports)
|
|
111
|
+
and not _calls_fixture(function)
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _scope_nodes(function: _Function) -> Iterator[ast.AST]:
|
|
116
|
+
pending: list[ast.AST] = list(reversed(function.body))
|
|
117
|
+
while pending:
|
|
118
|
+
node = pending.pop()
|
|
119
|
+
yield node
|
|
120
|
+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Lambda)):
|
|
121
|
+
pending.extend(reversed(list(ast.iter_child_nodes(node))))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _has_oracle(function: _Function, helpers: frozenset[str], imports: ImportIndex) -> bool:
|
|
125
|
+
for node in _scope_nodes(function):
|
|
126
|
+
if isinstance(node, (ast.Assert, ast.Raise)):
|
|
127
|
+
return True
|
|
128
|
+
if isinstance(node, ast.Call):
|
|
129
|
+
if names_verification(node.func, helpers):
|
|
130
|
+
return True
|
|
131
|
+
if isinstance(node.func, ast.Attribute) and node.func.attr in helpers:
|
|
132
|
+
return True
|
|
133
|
+
qualified = imports.resolved_qualified_name(node.func)
|
|
134
|
+
if qualified is not None and reads_as_verification(qualified.rsplit(".", 1)[-1]):
|
|
135
|
+
return True
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _oracle_helpers(tree: ast.Module, imports: ImportIndex) -> frozenset[str]:
|
|
140
|
+
functions = [node for node in ast.walk(tree) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
|
|
141
|
+
helpers: frozenset[str] = frozenset()
|
|
142
|
+
while True:
|
|
143
|
+
updated = helpers | frozenset(
|
|
144
|
+
function.name for function in functions if _has_oracle(function, helpers, imports)
|
|
145
|
+
)
|
|
146
|
+
if updated == helpers:
|
|
147
|
+
return helpers
|
|
148
|
+
helpers = updated
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _opts_out(statements: Sequence[ast.stmt]) -> bool:
|
|
152
|
+
return any(
|
|
153
|
+
isinstance(target, ast.Name)
|
|
154
|
+
and target.id == "__test__"
|
|
155
|
+
and (not isinstance(value, ast.Constant) or value.value is not True)
|
|
156
|
+
for target, value in _assignments(statements)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _assignments(statements: Sequence[ast.stmt]) -> Iterator[tuple[ast.expr, ast.expr]]:
|
|
161
|
+
for statement in statements:
|
|
162
|
+
if isinstance(statement, ast.Assign):
|
|
163
|
+
for target in statement.targets:
|
|
164
|
+
yield target, statement.value
|
|
165
|
+
elif isinstance(statement, ast.AnnAssign) and statement.value is not None:
|
|
166
|
+
yield statement.target, statement.value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _scope_marks(statements: Sequence[ast.stmt], imports: ImportIndex) -> bool:
|
|
170
|
+
for target, value in _assignments(statements):
|
|
171
|
+
if isinstance(target, ast.Name) and target.id == "pytestmark":
|
|
172
|
+
marks = value.elts if isinstance(value, (ast.List, ast.Tuple)) else [value]
|
|
173
|
+
if not _safe_marks(marks, imports):
|
|
174
|
+
return False
|
|
175
|
+
return True
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _disabled_tests(statements: Sequence[ast.stmt]) -> frozenset[str]:
|
|
179
|
+
return frozenset(
|
|
180
|
+
ast.unparse(target.value)
|
|
181
|
+
for target, value in _assignments(statements)
|
|
182
|
+
if (
|
|
183
|
+
isinstance(target, ast.Attribute)
|
|
184
|
+
and target.attr == "__test__"
|
|
185
|
+
and (not isinstance(value, ast.Constant) or value.value is not True)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _safe_marks(decorators: Sequence[ast.expr], imports: ImportIndex) -> bool:
|
|
191
|
+
for decorator in decorators:
|
|
192
|
+
target = decorator.func if isinstance(decorator, ast.Call) else decorator
|
|
193
|
+
qualified = imports.resolved_qualified_name(target)
|
|
194
|
+
if qualified is None or not qualified.startswith("pytest.mark."):
|
|
195
|
+
return False
|
|
196
|
+
if qualified.rsplit(".", 1)[-1] in _NON_UNIT_MARKS:
|
|
197
|
+
return False
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _collected_tests(tree: ast.Module, imports: ImportIndex) -> Iterator[_Function]:
|
|
202
|
+
disabled = _disabled_tests(tree.body)
|
|
203
|
+
for statement in tree.body:
|
|
204
|
+
if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
205
|
+
if _test_function(statement, disabled, imports):
|
|
206
|
+
yield statement
|
|
207
|
+
elif isinstance(statement, ast.ClassDef) and statement.name not in disabled and _test_class(statement, imports):
|
|
208
|
+
yield from _class_tests(statement, disabled, imports)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _class_tests(node: ast.ClassDef, disabled: frozenset[str], imports: ImportIndex) -> Iterator[_Function]:
|
|
212
|
+
class_disabled = _disabled_tests(node.body) | frozenset(
|
|
213
|
+
name.removeprefix(f"{node.name}.") for name in disabled if name.startswith(f"{node.name}.")
|
|
214
|
+
)
|
|
215
|
+
for child in node.body:
|
|
216
|
+
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)) and _test_function(
|
|
217
|
+
child, class_disabled, imports
|
|
218
|
+
):
|
|
219
|
+
yield child
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _test_function(node: _Function, disabled: frozenset[str], imports: ImportIndex) -> bool:
|
|
223
|
+
return node.name.startswith("test_") and node.name not in disabled and _safe_marks(node.decorator_list, imports)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _test_class(node: ast.ClassDef, imports: ImportIndex) -> bool:
|
|
227
|
+
if not node.name.startswith("Test") or node.bases or node.keywords or _opts_out(node.body):
|
|
228
|
+
return False
|
|
229
|
+
if any(
|
|
230
|
+
isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)) and child.name in {"__init__", "__new__"}
|
|
231
|
+
for child in node.body
|
|
232
|
+
):
|
|
233
|
+
return False
|
|
234
|
+
return _safe_marks(node.decorator_list, imports) and _scope_marks(node.body, imports)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _calls_fixture(function: _Function) -> bool:
|
|
238
|
+
parameters = {
|
|
239
|
+
argument.arg for argument in (*function.args.posonlyargs, *function.args.args, *function.args.kwonlyargs)
|
|
240
|
+
}
|
|
241
|
+
return any(
|
|
242
|
+
isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id in parameters
|
|
243
|
+
for node in _scope_nodes(function)
|
|
244
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_analysis_session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/interpreter_argv.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_position.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_closed_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_copy_on_write.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_dict_key_views.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_local_source.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_namespace_access.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_nominal_project.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_set_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_static_json_alias.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_assertions.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_composition.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_provenance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_dunder_all.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_select_star.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_class_row.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_str_enum.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|