sarj-python-lint 1.19.0__tar.gz → 1.20.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.19.0 → sarj_python_lint-1.20.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/pyproject.toml +1 -1
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_registry.py +2 -0
- sarj_python_lint-1.20.0/src/sarj_python_lint/rules/prefer_native_string_check.py +169 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/.gitignore +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/LICENSE +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/README.md +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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.20.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/
|
|
@@ -129,6 +129,7 @@ from sarj_python_lint.rules.prefer_monotonic_for_elapsed_time import PreferMonot
|
|
|
129
129
|
from sarj_python_lint.rules.prefer_namedtuple_over_tuple_return import (
|
|
130
130
|
PreferNamedtupleOverTupleReturn,
|
|
131
131
|
)
|
|
132
|
+
from sarj_python_lint.rules.prefer_native_string_check import PreferNativeStringCheck
|
|
132
133
|
from sarj_python_lint.rules.prefer_nominal_id_types import PreferNominalIdTypes
|
|
133
134
|
from sarj_python_lint.rules.prefer_non_nullable_collection import (
|
|
134
135
|
PreferNonNullableCollection,
|
|
@@ -298,6 +299,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
298
299
|
PreferWalrusStreamLoop.id: PreferWalrusStreamLoop,
|
|
299
300
|
PreferSelfTypeAnnotation.id: PreferSelfTypeAnnotation,
|
|
300
301
|
PreferSetIsdisjoint.id: PreferSetIsdisjoint,
|
|
302
|
+
PreferNativeStringCheck.id: PreferNativeStringCheck,
|
|
301
303
|
PreferSelfDocumentingConstant.id: PreferSelfDocumentingConstant,
|
|
302
304
|
NoDuplicateDunderAllEntry.id: NoDuplicateDunderAllEntry,
|
|
303
305
|
NoDunderAll.id: NoDunderAll,
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from collections import Counter
|
|
5
|
+
from pathlib import PurePosixPath
|
|
6
|
+
from typing import TYPE_CHECKING, ClassVar, final, override
|
|
7
|
+
|
|
8
|
+
from sarj_python_lint.rule_base import (
|
|
9
|
+
AutofixPolicy,
|
|
10
|
+
Diagnostic,
|
|
11
|
+
ExampleFile,
|
|
12
|
+
ExampleOutcome,
|
|
13
|
+
Rule,
|
|
14
|
+
RuleCategory,
|
|
15
|
+
RuleDocumentation,
|
|
16
|
+
RuleExample,
|
|
17
|
+
Severity,
|
|
18
|
+
is_suppressed,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from sarj_python_lint._file_context import PythonFileContext
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_PYDANTIC = frozenset({"pydantic"})
|
|
27
|
+
_BUILTINS = frozenset({"builtins"})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@final
|
|
31
|
+
class PreferNativeStringCheck(Rule):
|
|
32
|
+
id = "prefer-native-string-check"
|
|
33
|
+
code = "SARJ480"
|
|
34
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
35
|
+
default_level=Severity.WARNING,
|
|
36
|
+
summary="Use native string assertions instead of unconstrained Pydantic coercion.",
|
|
37
|
+
rationale="An unconstrained TypeAdapter(str).validate_python(...) obscures a simple string check and can decode bytes instead of rejecting them.",
|
|
38
|
+
remediation="Use isinstance(value, str) and the appropriate failure path; in tests, assert the type. Keep structured validation at the model or JSON boundary. Preserve intentional coercion or ValidationError contracts explicitly.",
|
|
39
|
+
category=RuleCategory.MAINTAINABILITY,
|
|
40
|
+
autofix=AutofixPolicy.NONE,
|
|
41
|
+
limitations=(
|
|
42
|
+
"Only plain built-in str adapters used by validate_python inside an assertion without validation options are reported. Configured adapters, constrained types, unions, models, containers, other scalar types and JSON/serialization operations are excluded.",
|
|
43
|
+
"Import aliases and uniquely assigned unconditional module-level adapter bindings are recognized. Local, conditional, reassigned, imported and indirect adapter values are not resolved; shadowed symbols are conservatively excluded.",
|
|
44
|
+
"Generated and vendored code are excluded. Intentional ValidationError or byte-decoding contracts need a local reasoned suppression; no automatic replacement can preserve those contracts.",
|
|
45
|
+
),
|
|
46
|
+
examples=(
|
|
47
|
+
RuleExample(
|
|
48
|
+
example_id="adapter-for-string-check",
|
|
49
|
+
title="A string assertion unnecessarily constructs a schema validator",
|
|
50
|
+
outcome=ExampleOutcome.MATCH,
|
|
51
|
+
files=(
|
|
52
|
+
ExampleFile.python(
|
|
53
|
+
"tests/test_receipt.py",
|
|
54
|
+
'from pydantic import TypeAdapter\ndef test_receipt():\n assert TypeAdapter(str).validate_python(receipt["id"]) == "abc"\n',
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
focus_path=PurePosixPath("tests/test_receipt.py"),
|
|
58
|
+
expected_count=1,
|
|
59
|
+
public=True,
|
|
60
|
+
),
|
|
61
|
+
RuleExample(
|
|
62
|
+
example_id="native-string-assertion",
|
|
63
|
+
title="A native assertion rejects unexpected types and narrows the value",
|
|
64
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
65
|
+
files=(
|
|
66
|
+
ExampleFile.python(
|
|
67
|
+
"tests/test_receipt.py",
|
|
68
|
+
'def test_receipt():\n value = receipt["id"]\n assert isinstance(value, str)\n assert value == "abc"\n',
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
focus_path=PurePosixPath("tests/test_receipt.py"),
|
|
72
|
+
expected_count=0,
|
|
73
|
+
public=True,
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
description = documentation.summary
|
|
78
|
+
|
|
79
|
+
@override
|
|
80
|
+
def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
|
|
81
|
+
if (
|
|
82
|
+
context.generated
|
|
83
|
+
or not {"vendor", "vendored", "third_party"}.isdisjoint(context.path.parts)
|
|
84
|
+
or "validate_python" not in context.source
|
|
85
|
+
or context.tree is None
|
|
86
|
+
):
|
|
87
|
+
return []
|
|
88
|
+
adapters = _module_adapters(context)
|
|
89
|
+
findings: list[Diagnostic] = []
|
|
90
|
+
for call in context.nodes(ast.Call):
|
|
91
|
+
if not isinstance(call.func, ast.Attribute) or call.func.attr != "validate_python" or call.keywords:
|
|
92
|
+
continue
|
|
93
|
+
if not _inside_assertion(call, context):
|
|
94
|
+
continue
|
|
95
|
+
receiver = call.func.value
|
|
96
|
+
if not (
|
|
97
|
+
_plain_string_adapter(receiver, context) or (isinstance(receiver, ast.Name) and receiver.id in adapters)
|
|
98
|
+
):
|
|
99
|
+
continue
|
|
100
|
+
if is_suppressed(context.source_lines, call.lineno, self.code):
|
|
101
|
+
continue
|
|
102
|
+
findings.append(
|
|
103
|
+
Diagnostic(
|
|
104
|
+
path=context.path,
|
|
105
|
+
line=call.lineno,
|
|
106
|
+
col=call.col_offset + 1,
|
|
107
|
+
code=self.code,
|
|
108
|
+
severity=Severity.WARNING,
|
|
109
|
+
message="plain str adapter used only to validate a Python value; use isinstance(value, str) with an explicit failure path, or preserve the intentional Pydantic contract explicitly",
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
return sorted(findings, key=lambda finding: (finding.line, finding.col))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _plain_string_adapter(node: ast.expr, context: PythonFileContext) -> bool:
|
|
116
|
+
if not isinstance(node, ast.Call):
|
|
117
|
+
return False
|
|
118
|
+
function = node.func.value if isinstance(node.func, ast.Subscript) else node.func
|
|
119
|
+
if not context.imports.resolves(function, sources=_PYDANTIC, symbol="TypeAdapter"):
|
|
120
|
+
return False
|
|
121
|
+
if len(node.args) == 1 and not node.keywords:
|
|
122
|
+
value = node.args[0]
|
|
123
|
+
elif not node.args and len(node.keywords) == 1 and node.keywords[0].arg == "type":
|
|
124
|
+
value = node.keywords[0].value
|
|
125
|
+
else:
|
|
126
|
+
return False
|
|
127
|
+
return (
|
|
128
|
+
isinstance(value, ast.Name) and value.id == "str" and context.imports.builtin_is_unshadowed("str")
|
|
129
|
+
) or context.imports.resolves(value, sources=_BUILTINS, symbol="str")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _module_adapters(context: PythonFileContext) -> frozenset[str]:
|
|
133
|
+
tree = context.tree
|
|
134
|
+
if tree is None:
|
|
135
|
+
return frozenset()
|
|
136
|
+
bindings = _binding_counts(context)
|
|
137
|
+
names: set[str] = set()
|
|
138
|
+
for statement in tree.body:
|
|
139
|
+
if isinstance(statement, ast.Assign) and len(statement.targets) == 1:
|
|
140
|
+
target, value = statement.targets[0], statement.value
|
|
141
|
+
elif isinstance(statement, ast.AnnAssign) and statement.value is not None:
|
|
142
|
+
target, value = statement.target, statement.value
|
|
143
|
+
else:
|
|
144
|
+
continue
|
|
145
|
+
if isinstance(target, ast.Name) and bindings[target.id] == 1 and _plain_string_adapter(value, context):
|
|
146
|
+
names.add(target.id)
|
|
147
|
+
return frozenset(names)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _binding_counts(context: PythonFileContext) -> Counter[str]:
|
|
151
|
+
bindings = Counter(node.id for node in context.nodes(ast.Name) if isinstance(node.ctx, ast.Store | ast.Del))
|
|
152
|
+
bindings.update(argument.arg for argument in context.nodes(ast.arg))
|
|
153
|
+
bindings.update(alias.asname or alias.name.split(".")[0] for alias in context.nodes(ast.alias))
|
|
154
|
+
bindings.update(node.name for node in context.nodes(ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
|
|
155
|
+
bindings.update(node.name for node in context.nodes(ast.ExceptHandler) if node.name is not None)
|
|
156
|
+
bindings.update(node.name for node in context.nodes(ast.MatchAs, ast.MatchStar) if node.name is not None)
|
|
157
|
+
bindings.update(node.rest for node in context.nodes(ast.MatchMapping) if node.rest is not None)
|
|
158
|
+
return bindings
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _inside_assertion(call: ast.Call, context: PythonFileContext) -> bool:
|
|
162
|
+
parent = context.parents.get(call)
|
|
163
|
+
while parent is not None:
|
|
164
|
+
if isinstance(parent, ast.Assert):
|
|
165
|
+
return True
|
|
166
|
+
if isinstance(parent, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
167
|
+
return False
|
|
168
|
+
parent = context.parents.get(parent)
|
|
169
|
+
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_ast_position.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_dict_key_views.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_local_source.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_nominal_project.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_set_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_test_assertions.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/_test_composition.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.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.19.0 → sarj_python_lint-1.20.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
|
{sarj_python_lint-1.19.0 → sarj_python_lint-1.20.0}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|