sarj-python-lint 0.87.5__tar.gz → 0.87.7__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-0.87.5 → sarj_python_lint-0.87.7}/PKG-INFO +1 -1
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/pyproject.toml +1 -1
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_registry.py +2 -0
- sarj_python_lint-0.87.7/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +225 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/.gitignore +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/LICENSE +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/README.md +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/require_typed_http_test_response.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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: 0.87.
|
|
3
|
+
Version: 0.87.7
|
|
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/
|
|
@@ -47,6 +47,7 @@ from sarj_python_lint.rules.no_file_level_escape_hatch_suppression import (
|
|
|
47
47
|
from sarj_python_lint.rules.no_first_party_private_import import (
|
|
48
48
|
NoFirstPartyPrivateImport,
|
|
49
49
|
)
|
|
50
|
+
from sarj_python_lint.rules.no_fixed_sleep_before_assert import NoFixedSleepBeforeAssert
|
|
50
51
|
from sarj_python_lint.rules.no_frozen_after_validator_field_write import (
|
|
51
52
|
NoFrozenAfterValidatorFieldWrite,
|
|
52
53
|
)
|
|
@@ -204,6 +205,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
204
205
|
NoRawSourceTextTestOracle.id: NoRawSourceTextTestOracle,
|
|
205
206
|
IacSourceCoupledTest.id: IacSourceCoupledTest,
|
|
206
207
|
NoRawConnectionInTests.id: NoRawConnectionInTests,
|
|
208
|
+
NoFixedSleepBeforeAssert.id: NoFixedSleepBeforeAssert,
|
|
207
209
|
NoAnalyticalAggregationInPostgresStore.id: NoAnalyticalAggregationInPostgresStore,
|
|
208
210
|
NoSelectStar.id: NoSelectStar,
|
|
209
211
|
NoServiceBehaviorInSettings.id: NoServiceBehaviorInSettings,
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from itertools import pairwise
|
|
5
|
+
from pathlib import PurePosixPath
|
|
6
|
+
import re
|
|
7
|
+
from typing import TYPE_CHECKING, ClassVar, Final, final, override
|
|
8
|
+
|
|
9
|
+
from sarj_python_lint.rule_base import (
|
|
10
|
+
AutofixPolicy,
|
|
11
|
+
Diagnostic,
|
|
12
|
+
ExampleFile,
|
|
13
|
+
ExampleOutcome,
|
|
14
|
+
Rule,
|
|
15
|
+
RuleCategory,
|
|
16
|
+
RuleDocumentation,
|
|
17
|
+
RuleExample,
|
|
18
|
+
Severity,
|
|
19
|
+
parse_or_none,
|
|
20
|
+
)
|
|
21
|
+
from sarj_python_lint.rules._imports import ImportIndex
|
|
22
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Iterator
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_ASYNC_SLEEPS: Final = frozenset({"asyncio.sleep", "anyio.sleep", "trio.sleep"})
|
|
31
|
+
_DURATION_KEYWORDS: Final = frozenset({"delay", "seconds"})
|
|
32
|
+
_CONSTANT_NAME: Final = re.compile(r"_*[A-Z][A-Z0-9_]*")
|
|
33
|
+
_MESSAGE: Final = (
|
|
34
|
+
"fixed sleep before an assertion guesses how long the code under test needs; await its signal or poll "
|
|
35
|
+
"the condition with a deadline"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
type _TestFunction = ast.FunctionDef | ast.AsyncFunctionDef
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@final
|
|
42
|
+
class NoFixedSleepBeforeAssert(Rule):
|
|
43
|
+
id = "no-fixed-sleep-before-assert"
|
|
44
|
+
code = "SARJ455"
|
|
45
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
46
|
+
default_level=Severity.WARNING,
|
|
47
|
+
summary="Do not sleep for a fixed interval and then assert in a test.",
|
|
48
|
+
rationale=(
|
|
49
|
+
"A fixed sleep followed by an assertion guesses how long the code under test needs. On a loaded CI "
|
|
50
|
+
"runner the guess is too short and the test flakes; on an idle one every run pays the whole interval."
|
|
51
|
+
),
|
|
52
|
+
remediation=(
|
|
53
|
+
"Await the signal the code exposes, such as an event, future, queue item, or fake callback, or poll the "
|
|
54
|
+
"condition with a deadline. When the test deliberately proves that nothing happens during a quiet "
|
|
55
|
+
"window, keep the sleep with an exact SARJ455 suppression that names the window."
|
|
56
|
+
),
|
|
57
|
+
category=RuleCategory.TESTING,
|
|
58
|
+
autofix=AutofixPolicy.NONE,
|
|
59
|
+
limitations=(
|
|
60
|
+
"Only pytest-collected test functions in test_*.py and *_test.py modules are inspected; fixtures, helpers, nested functions, and conftest.py are not.",
|
|
61
|
+
"A sleep is reported only when the next statement in the same block is an assert, so an assert placed after the enclosing with, if, or try block is not reported.",
|
|
62
|
+
"A duration is fixed only when built from numeric literals and UPPER_CASE constants; durations read from other names or from calls are not reported.",
|
|
63
|
+
"Sleeps inside loops are treated as polling, and time.sleep in an async test is left to Ruff ASYNC251.",
|
|
64
|
+
),
|
|
65
|
+
examples=(
|
|
66
|
+
RuleExample(
|
|
67
|
+
example_id="test-sleeps-then-asserts",
|
|
68
|
+
title="A test waits a guessed interval before asserting",
|
|
69
|
+
outcome=ExampleOutcome.MATCH,
|
|
70
|
+
files=(
|
|
71
|
+
ExampleFile.python(
|
|
72
|
+
"tests/test_monitor.py",
|
|
73
|
+
"import asyncio\n\n\nasync def test_reports_silence(monitor):\n monitor.start()\n"
|
|
74
|
+
" await asyncio.sleep(0.5)\n assert monitor.reason == 'silence'\n",
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
focus_path=PurePosixPath("tests/test_monitor.py"),
|
|
78
|
+
expected_count=1,
|
|
79
|
+
public=True,
|
|
80
|
+
),
|
|
81
|
+
RuleExample(
|
|
82
|
+
example_id="test-awaits-signal-then-asserts",
|
|
83
|
+
title="A test awaits the signal with a deadline before asserting",
|
|
84
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
85
|
+
files=(
|
|
86
|
+
ExampleFile.python(
|
|
87
|
+
"tests/test_monitor.py",
|
|
88
|
+
"import asyncio\n\n\nasync def test_reports_silence(monitor):\n monitor.start()\n"
|
|
89
|
+
" await asyncio.wait_for(monitor.fired.wait(), timeout=5)\n"
|
|
90
|
+
" assert monitor.reason == 'silence'\n",
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
focus_path=PurePosixPath("tests/test_monitor.py"),
|
|
94
|
+
expected_count=0,
|
|
95
|
+
public=True,
|
|
96
|
+
),
|
|
97
|
+
RuleExample(
|
|
98
|
+
example_id="test-blocks-then-asserts",
|
|
99
|
+
title="A synchronous test blocks for a guessed interval before asserting",
|
|
100
|
+
outcome=ExampleOutcome.MATCH,
|
|
101
|
+
files=(
|
|
102
|
+
ExampleFile.python(
|
|
103
|
+
"tests/test_worker.py",
|
|
104
|
+
"import time\n\n\ndef test_worker_drains_queue(worker, queue):\n worker.start()\n"
|
|
105
|
+
" time.sleep(1)\n assert queue.pending() == 0\n",
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
focus_path=PurePosixPath("tests/test_worker.py"),
|
|
109
|
+
expected_count=1,
|
|
110
|
+
scenario="blocking-sleep",
|
|
111
|
+
public=True,
|
|
112
|
+
),
|
|
113
|
+
RuleExample(
|
|
114
|
+
example_id="test-polls-with-deadline-then-asserts",
|
|
115
|
+
title="A synchronous test polls the condition with a deadline before asserting",
|
|
116
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
117
|
+
files=(
|
|
118
|
+
ExampleFile.python(
|
|
119
|
+
"tests/test_worker.py",
|
|
120
|
+
"import time\n\n\ndef test_worker_drains_queue(worker, queue):\n worker.start()\n"
|
|
121
|
+
" deadline = time.monotonic() + 5\n"
|
|
122
|
+
" while queue.pending() and time.monotonic() < deadline:\n time.sleep(0.01)\n"
|
|
123
|
+
" assert queue.pending() == 0\n",
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
focus_path=PurePosixPath("tests/test_worker.py"),
|
|
127
|
+
expected_count=0,
|
|
128
|
+
scenario="blocking-sleep",
|
|
129
|
+
public=True,
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
description = documentation.summary
|
|
134
|
+
|
|
135
|
+
@override
|
|
136
|
+
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
137
|
+
if not _is_collected_test_module(path) or "sleep" not in source or is_generated(path, source):
|
|
138
|
+
return []
|
|
139
|
+
tree = parse_or_none(path, source)
|
|
140
|
+
if tree is None:
|
|
141
|
+
return []
|
|
142
|
+
imports = ImportIndex.from_tree(tree)
|
|
143
|
+
diagnostics = [
|
|
144
|
+
Diagnostic(path=path, line=statement.lineno, col=statement.col_offset + 1, code=self.code, message=_MESSAGE)
|
|
145
|
+
for test in _collected_tests(tree.body)
|
|
146
|
+
for statement, following in _adjacent_statements(test.body)
|
|
147
|
+
if isinstance(following, ast.Assert) and _is_fixed_sleep(statement, test, imports)
|
|
148
|
+
]
|
|
149
|
+
return sorted(diagnostics, key=lambda item: (item.line, item.col))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _is_collected_test_module(path: Path) -> bool:
|
|
153
|
+
return path.suffix == ".py" and (path.stem.startswith("test_") or path.stem.endswith("_test"))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _collected_tests(statements: list[ast.stmt]) -> Iterator[_TestFunction]:
|
|
157
|
+
for statement in statements:
|
|
158
|
+
match statement:
|
|
159
|
+
case ast.FunctionDef() | ast.AsyncFunctionDef() if statement.name.startswith("test"):
|
|
160
|
+
yield statement
|
|
161
|
+
case ast.ClassDef() if statement.name.startswith("Test"):
|
|
162
|
+
yield from _collected_tests(statement.body)
|
|
163
|
+
case _:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _adjacent_statements(block: list[ast.stmt]) -> Iterator[tuple[ast.stmt, ast.stmt]]:
|
|
168
|
+
yield from pairwise(block)
|
|
169
|
+
for statement in block:
|
|
170
|
+
for nested in _straight_line_blocks(statement):
|
|
171
|
+
yield from _adjacent_statements(nested)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _straight_line_blocks(statement: ast.stmt) -> list[list[ast.stmt]]:
|
|
175
|
+
match statement:
|
|
176
|
+
case ast.If(body=body, orelse=orelse):
|
|
177
|
+
return [body, orelse]
|
|
178
|
+
case ast.With(body=body) | ast.AsyncWith(body=body):
|
|
179
|
+
return [body]
|
|
180
|
+
case (
|
|
181
|
+
ast.Try(body=body, handlers=handlers, orelse=orelse, finalbody=finalbody)
|
|
182
|
+
| ast.TryStar(body=body, handlers=handlers, orelse=orelse, finalbody=finalbody)
|
|
183
|
+
):
|
|
184
|
+
return [body, *(handler.body for handler in handlers), orelse, finalbody]
|
|
185
|
+
case ast.Match(cases=cases):
|
|
186
|
+
return [case.body for case in cases]
|
|
187
|
+
case _:
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _is_fixed_sleep(statement: ast.stmt, test: _TestFunction, imports: ImportIndex) -> bool:
|
|
192
|
+
match statement:
|
|
193
|
+
case ast.Expr(value=ast.Await(value=ast.Call() as call)):
|
|
194
|
+
return imports.resolved_qualified_name(call.func) in _ASYNC_SLEEPS and _has_fixed_duration(call)
|
|
195
|
+
case ast.Expr(value=ast.Call() as call) if isinstance(test, ast.FunctionDef):
|
|
196
|
+
return imports.resolved_qualified_name(call.func) == "time.sleep" and _has_fixed_duration(call)
|
|
197
|
+
case _:
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _has_fixed_duration(call: ast.Call) -> bool:
|
|
202
|
+
duration = (
|
|
203
|
+
call.args[0]
|
|
204
|
+
if call.args
|
|
205
|
+
else next((keyword.value for keyword in call.keywords if keyword.arg in _DURATION_KEYWORDS), None)
|
|
206
|
+
)
|
|
207
|
+
match duration:
|
|
208
|
+
case None | ast.Constant(value=0):
|
|
209
|
+
return False
|
|
210
|
+
case _:
|
|
211
|
+
return _is_fixed(duration)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _is_fixed(node: ast.expr) -> bool:
|
|
215
|
+
match node:
|
|
216
|
+
case ast.Constant(value=int() | float()):
|
|
217
|
+
return True
|
|
218
|
+
case ast.Name(id=name) | ast.Attribute(attr=name):
|
|
219
|
+
return _CONSTANT_NAME.fullmatch(name) is not None
|
|
220
|
+
case ast.UnaryOp(op=ast.UAdd() | ast.USub(), operand=operand):
|
|
221
|
+
return _is_fixed(operand)
|
|
222
|
+
case ast.BinOp(left=left, op=ast.Add() | ast.Sub() | ast.Mult() | ast.Div() | ast.FloorDiv(), right=right):
|
|
223
|
+
return _is_fixed(left) and _is_fixed(right)
|
|
224
|
+
case _:
|
|
225
|
+
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_ast_position.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/_test_assertions.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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/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
|
{sarj_python_lint-0.87.5 → sarj_python_lint-0.87.7}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|