sarj-python-lint 1.14.0__tar.gz → 1.14.2__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.
Files changed (171) hide show
  1. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_registry.py +2 -0
  4. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +48 -8
  5. sarj_python_lint-1.14.2/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +168 -0
  6. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/.gitignore +0 -0
  7. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/LICENSE +0 -0
  8. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/README.md +0 -0
  9. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/__init__.py +0 -0
  10. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/__main__.py +0 -0
  11. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_analysis_session.py +0 -0
  12. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_file_context.py +0 -0
  13. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_filesystem.py +0 -0
  14. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_python_target.py +0 -0
  15. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  16. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_secret_names.py +0 -0
  17. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/_version.py +0 -0
  18. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/benchmark.py +0 -0
  19. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/json_boundary.py +0 -0
  20. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/py.typed +0 -0
  21. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/ratchet.py +0 -0
  22. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rule_base.py +0 -0
  23. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/__init__.py +0 -0
  24. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  25. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  26. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  27. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_comments.py +0 -0
  28. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  29. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  30. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  31. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  32. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_first_party.py +0 -0
  33. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  34. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_imports.py +0 -0
  35. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_logging.py +0 -0
  36. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  37. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_paths.py +0 -0
  38. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_project_index.py +0 -0
  39. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  40. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_pytest.py +0 -0
  41. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  42. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  43. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_sql.py +0 -0
  44. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  45. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  46. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  47. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  48. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  49. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  50. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  51. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +0 -0
  52. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  53. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  54. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  55. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  56. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  57. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  58. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  59. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  60. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  61. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  62. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  63. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  64. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  65. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  66. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  67. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  68. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  69. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  70. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  71. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  72. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  73. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  74. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  75. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  76. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  77. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  78. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  79. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  80. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  81. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  82. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  83. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  84. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  85. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  86. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  87. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  88. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  89. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  90. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  91. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  92. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  93. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  94. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  95. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  96. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  97. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  98. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  99. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  100. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  101. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  102. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  103. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  104. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  105. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  106. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  107. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  108. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  109. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  110. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  111. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  112. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  113. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  114. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  115. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  116. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  117. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  118. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  119. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  120. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  121. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  122. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  123. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  124. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  125. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  126. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  127. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  128. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  129. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  130. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  131. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  132. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  133. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  134. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  135. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  136. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  137. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  138. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  139. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  140. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  141. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  142. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  143. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  144. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  145. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  146. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  147. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  148. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  149. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  150. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  151. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  152. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  153. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  154. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  155. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  156. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  157. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  158. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  159. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  160. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  161. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  162. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/stepdown.py +0 -0
  163. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  164. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  165. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  166. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  167. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  168. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  169. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  170. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  171. {sarj_python_lint-1.14.0 → sarj_python_lint-1.14.2}/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.14.0
3
+ Version: 1.14.2
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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.14.0"
3
+ version = "1.14.2"
4
4
  description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "sarj-ai" }]
@@ -88,6 +88,7 @@ from sarj_python_lint.rules.no_unique_violation_message_match import (
88
88
  NoUniqueViolationMessageMatch,
89
89
  )
90
90
  from sarj_python_lint.rules.no_unnecessary_docstring import NoUnnecessaryDocstring
91
+ from sarj_python_lint.rules.no_unused_underscored_keyword_parameter import NoUnusedUnderscoredKeywordParameter
91
92
  from sarj_python_lint.rules.no_unused_value_marker import NoUnusedValueMarker
92
93
  from sarj_python_lint.rules.no_vague_suppression_description import (
93
94
  NoVagueSuppressionDescription,
@@ -306,6 +307,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
306
307
  NoTypedDocSections.id: NoTypedDocSections,
307
308
  NoUnnecessaryDocstring.id: NoUnnecessaryDocstring,
308
309
  NoUnusedValueMarker.id: NoUnusedValueMarker,
310
+ NoUnusedUnderscoredKeywordParameter.id: NoUnusedUnderscoredKeywordParameter,
309
311
  PreferNominalIdTypes.id: PreferNominalIdTypes,
310
312
  NoAnyMappingTypes.id: NoAnyMappingTypes,
311
313
  NoUniqueViolationMessageMatch.id: NoUniqueViolationMessageMatch,
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import ast
4
+ from collections import Counter
4
5
  from dataclasses import dataclass
5
6
  from pathlib import PurePosixPath
6
7
  from typing import TYPE_CHECKING, ClassVar, final, override
@@ -48,7 +49,7 @@ _FALLBACK_VALUES = 2
48
49
  @dataclass(frozen=True, slots=True)
49
50
  class _ScopeUsage:
50
51
  parents: dict[ast.AST, ast.AST]
51
- called: set[str]
52
+ calls: dict[str, list[ast.Call]]
52
53
 
53
54
 
54
55
  @final
@@ -66,6 +67,7 @@ class NoNullableDependencyFallback(Rule):
66
67
  limitations=(
67
68
  "Warns for None-defaulted function parameters with a fallback used as a callable or a proven application-settings fallback; genuine absent state is allowed.",
68
69
  "Constructors remain owned by SARJ095 and SARJ468. Decorated functions, tests, generated code, nested scope captures, non-callable object fallbacks, and interprocedural forwarding are excluded.",
70
+ "Callable use through a local assignment, annotation or None guard requires at most one body binding for that name and a call after the binding statement completes. Rebinding, deletion, imports, definitions, pattern or exception captures, global/nonlocal declarations and nested-scope bindings conservatively exclude that inference; direct inline calls and proven settings fallbacks remain independent.",
69
71
  "No autofix: removing explicit None acceptance changes the callable contract, and moving a fallback can change initialization timing or error handling.",
70
72
  ),
71
73
  examples=(
@@ -163,6 +165,8 @@ def _hidden_dependencies(
163
165
  guard = _guarded_fallback(statement, candidates)
164
166
  bindings = LocalBindingCollector()
165
167
  bindings.visit(statement)
168
+ bindings.names.update(name for node in _scope_nodes(statement) for name in _binding_names(node))
169
+ bindings.names.update(node.target.id for node in ast.walk(statement) if isinstance(node, ast.NamedExpr))
166
170
  # Branch/loop bindings are conservatively local; do not infer data flow.
167
171
  if not isinstance(statement, (ast.Assign, ast.AnnAssign)) and guard is None:
168
172
  candidates.difference_update(bindings.names)
@@ -177,7 +181,7 @@ def _hidden_dependencies(
177
181
  )
178
182
  if guard is not None:
179
183
  name, value = guard
180
- if name in usage.called or _is_settings_fallback(value, resolver, shadowed):
184
+ if _called_after(name, statement, usage) or _is_settings_fallback(value, resolver, shadowed):
181
185
  hidden.add(name)
182
186
  candidates.difference_update(bindings.names)
183
187
  return hidden
@@ -186,8 +190,41 @@ def _hidden_dependencies(
186
190
  def _scope_usage(function: ast.FunctionDef | ast.AsyncFunctionDef) -> _ScopeUsage:
187
191
  nodes = [node for statement in function.body for node in _scope_nodes(statement)]
188
192
  parents = {child: node for node in nodes for child in ast.iter_child_nodes(node)}
189
- called = {node.func.id for node in nodes if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)}
190
- return _ScopeUsage(parents=parents, called=called)
193
+ bindings = Counter(
194
+ name for statement in function.body for node in ast.walk(statement) for name in _binding_names(node)
195
+ )
196
+ calls: dict[str, list[ast.Call]] = {}
197
+ for node in nodes:
198
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and bindings[node.func.id] <= 1:
199
+ calls.setdefault(node.func.id, []).append(node)
200
+ return _ScopeUsage(parents=parents, calls=calls)
201
+
202
+
203
+ def _called_after(name: str, binding: ast.stmt, usage: _ScopeUsage) -> bool:
204
+ end = (binding.end_lineno or binding.lineno, binding.end_col_offset or binding.col_offset)
205
+ return any((call.lineno, call.col_offset) > end for call in usage.calls.get(name, ()))
206
+
207
+
208
+ def _binding_names(node: ast.AST) -> Iterator[str]:
209
+ match node:
210
+ case (
211
+ ast.Name(id=name, ctx=(ast.Store() | ast.Del()))
212
+ | ast.arg(arg=name)
213
+ | ast.FunctionDef(name=name)
214
+ | ast.AsyncFunctionDef(name=name)
215
+ | ast.ClassDef(name=name)
216
+ | ast.ExceptHandler(name=str() as name)
217
+ | ast.MatchAs(name=str() as name)
218
+ | ast.MatchStar(name=str() as name)
219
+ | ast.MatchMapping(rest=str() as name)
220
+ ):
221
+ yield name
222
+ case ast.alias(name=imported, asname=alias):
223
+ yield alias or imported.partition(".")[0]
224
+ case ast.Global(names=names) | ast.Nonlocal(names=names):
225
+ yield from names
226
+ case _:
227
+ return
191
228
 
192
229
 
193
230
  def _expression_dependencies(
@@ -270,7 +307,10 @@ def _used_as_dependency(node: ast.AST, usage: _ScopeUsage) -> bool:
270
307
  parent = usage.parents.get(node)
271
308
  if isinstance(parent, ast.Call) and parent.func is node:
272
309
  return True
273
- if isinstance(parent, ast.Assign) and parent.value is node and len(parent.targets) == 1:
274
- target = parent.targets[0]
275
- return isinstance(target, ast.Name) and target.id in usage.called
276
- return False
310
+ match parent:
311
+ case (
312
+ ast.Assign(targets=[ast.Name(id=name)], value=value) | ast.AnnAssign(target=ast.Name(id=name), value=value)
313
+ ) if value is node:
314
+ return _called_after(name, parent, usage)
315
+ case _:
316
+ return False
@@ -0,0 +1,168 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from itertools import chain
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
+ @final
27
+ class NoUnusedUnderscoredKeywordParameter(Rule):
28
+ id = "no-unused-underscored-keyword-parameter"
29
+ code = "SARJ475"
30
+ documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
31
+ default_level=Severity.WARNING,
32
+ summary="Remove unused underscore-prefixed keyword-only parameters instead of hiding them from lint.",
33
+ rationale=(
34
+ "A keyword-only parameter requires callers to use its declared name. Prefixing an unused input with an "
35
+ "underscore silences unused-argument lint while retaining an input whose value has no effect. Removing "
36
+ "the dead input and its callers keeps the callable's interface honest."
37
+ ),
38
+ remediation=(
39
+ "Remove the unused keyword-only parameter and update callers together. If an external contract requires "
40
+ "the input, preserve its required spelling and use a narrow, reasoned unused-argument suppression."
41
+ ),
42
+ category=RuleCategory.MAINTAINABILITY,
43
+ autofix=AutofixPolicy.NONE,
44
+ limitations=(
45
+ "Only underscore-prefixed keyword-only declarations are checked; positional and variadic callback slots are excluded.",
46
+ "Decorated functions other than unshadowed builtin staticmethod/classmethod, inherited methods, and intentional stubs are excluded.",
47
+ "Any reference within the body counts as use, including nested scopes; shadowing and reassignment can produce false negatives.",
48
+ "Functions accessing locals, vars, eval, or exec through recognized builtin bindings are excluded.",
49
+ "Call sites, lambda parameters, unknown dynamic reflection, and cross-module callback contracts are not resolved.",
50
+ "Generated and vendored files are excluded; test source is checked.",
51
+ ),
52
+ examples=(
53
+ RuleExample(
54
+ example_id="discarded-keyword-input",
55
+ title="An ignored keyword input is kept just to silence unused-argument lint",
56
+ outcome=ExampleOutcome.MATCH,
57
+ files=(ExampleFile.python("app/sink.py", "def emit(*, _email=None):\n return 'ready'\n"),),
58
+ focus_path=PurePosixPath("app/sink.py"),
59
+ expected_count=1,
60
+ public=True,
61
+ ),
62
+ RuleExample(
63
+ example_id="removed-keyword-input",
64
+ title="The callable exposes only inputs it uses",
65
+ outcome=ExampleOutcome.NO_MATCH,
66
+ files=(ExampleFile.python("app/sink.py", "def emit():\n return 'ready'\n"),),
67
+ focus_path=PurePosixPath("app/sink.py"),
68
+ expected_count=0,
69
+ public=True,
70
+ ),
71
+ ),
72
+ )
73
+ description = documentation.summary
74
+
75
+ @override
76
+ def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
77
+ if context.generated or "_" not in context.source or context.tree is None:
78
+ return []
79
+ findings: list[Diagnostic] = []
80
+ for function in context.nodes(ast.FunctionDef, ast.AsyncFunctionDef):
81
+ for argument in _unused_parameters(context, function):
82
+ if is_suppressed(context.source_lines, argument.lineno, self.code):
83
+ continue
84
+ findings.append(
85
+ Diagnostic(
86
+ path=context.path,
87
+ line=argument.lineno,
88
+ col=argument.col_offset + 1,
89
+ code=self.code,
90
+ severity=Severity.WARNING,
91
+ message=(
92
+ f"keyword-only parameter `{argument.arg}` is never referenced; remove the unused input "
93
+ "and its callers instead of hiding it from unused-argument lint"
94
+ ),
95
+ )
96
+ )
97
+ return sorted(findings, key=lambda item: (item.line, item.col))
98
+
99
+
100
+ def _unused_parameters(
101
+ context: PythonFileContext, function: ast.FunctionDef | ast.AsyncFunctionDef
102
+ ) -> tuple[ast.arg, ...]:
103
+ candidates = [argument for argument in function.args.kwonlyargs if argument.arg.startswith("_")]
104
+ if not candidates or _has_contract(context, function) or _is_stub(function):
105
+ return ()
106
+ body = list(chain.from_iterable(ast.walk(statement) for statement in function.body))
107
+ if _reads_dynamic_locals(context, body):
108
+ return ()
109
+ references = {node.id for node in body if isinstance(node, ast.Name)}
110
+ return tuple(argument for argument in candidates if argument.arg not in references)
111
+
112
+
113
+ def _has_contract(context: PythonFileContext, function: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
114
+ parent = context.parents.get(function)
115
+ while parent is not None and not isinstance(
116
+ parent, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef | ast.Module
117
+ ):
118
+ parent = context.parents.get(parent)
119
+ if isinstance(parent, ast.ClassDef) and parent.bases:
120
+ return True
121
+ return any(
122
+ not (
123
+ isinstance(decorator, ast.Name)
124
+ and decorator.id in {"staticmethod", "classmethod"}
125
+ and context.imports.builtin_is_unshadowed(decorator.id)
126
+ )
127
+ for decorator in function.decorator_list
128
+ )
129
+
130
+
131
+ def _is_stub(function: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
132
+ body = function.body
133
+ if (
134
+ body
135
+ and isinstance(body[0], ast.Expr)
136
+ and isinstance(body[0].value, ast.Constant)
137
+ and isinstance(body[0].value.value, str)
138
+ ):
139
+ body = body[1:]
140
+ if len(body) != 1:
141
+ return False
142
+ match body[0]:
143
+ case ast.Pass() | ast.Return(value=None) | ast.Return(value=ast.Constant(value=None)):
144
+ return True
145
+ case ast.Expr(value=ast.Constant(value=value)):
146
+ return value is Ellipsis
147
+ case ast.Raise(exc=exception):
148
+ if isinstance(exception, ast.Call):
149
+ exception = exception.func
150
+ return isinstance(exception, ast.Name) and exception.id == "NotImplementedError"
151
+ case _:
152
+ return False
153
+
154
+
155
+ def _reads_dynamic_locals(context: PythonFileContext, body: list[ast.AST]) -> bool:
156
+ return any(
157
+ isinstance(node, ast.expr)
158
+ and (
159
+ (
160
+ isinstance(node, ast.Name)
161
+ and node.id in {"locals", "vars", "eval", "exec"}
162
+ and context.module_imports.builtin_is_unshadowed(node.id)
163
+ )
164
+ or context.module_imports.resolved_symbol(node, sources=frozenset({"builtins"}))
165
+ in {"locals", "vars", "eval", "exec"}
166
+ )
167
+ for node in body
168
+ )