sarj-python-lint 0.87.7__tar.gz → 0.87.9__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 (144) hide show
  1. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/PKG-INFO +2 -2
  2. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/pyproject.toml +3 -3
  3. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_secret_in_log.py +33 -4
  4. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/.gitignore +0 -0
  5. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/LICENSE +0 -0
  6. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/README.md +0 -0
  7. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/__init__.py +0 -0
  8. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/__main__.py +0 -0
  9. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_analysis_session.py +0 -0
  10. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_filesystem.py +0 -0
  11. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_python_target.py +0 -0
  12. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  13. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_secret_names.py +0 -0
  14. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/_version.py +0 -0
  15. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/json_boundary.py +0 -0
  16. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/py.typed +0 -0
  17. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/ratchet.py +0 -0
  18. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rule_base.py +0 -0
  19. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/__init__.py +0 -0
  20. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  21. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  22. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  23. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_comments.py +0 -0
  24. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  25. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  26. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_first_party.py +0 -0
  27. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  28. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_imports.py +0 -0
  29. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_logging.py +0 -0
  30. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_paths.py +0 -0
  31. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_project_index.py +0 -0
  32. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  33. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_pytest.py +0 -0
  34. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_registry.py +0 -0
  35. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_sql.py +0 -0
  36. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  37. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  38. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  39. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  40. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  41. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  42. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  43. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  44. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  45. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  46. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  47. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  48. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  49. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  50. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  51. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  52. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  53. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  54. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  55. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  56. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  57. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  58. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  59. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  60. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  61. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  62. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  63. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  64. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  65. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  66. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  67. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  68. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  69. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  70. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  71. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  72. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  73. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  74. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  75. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  76. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  77. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  78. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  79. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  80. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  81. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  82. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  83. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  84. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  85. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  86. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  87. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  88. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  89. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  90. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  91. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  92. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  93. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  94. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  95. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  96. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  97. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  98. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  99. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  100. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  101. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  102. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  103. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  104. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  105. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  106. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  107. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  108. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  109. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  110. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  111. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  112. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  113. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  114. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  115. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  116. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  117. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  118. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  119. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  120. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  121. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  122. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  123. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  124. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  125. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  126. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  127. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  128. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  129. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  130. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  131. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  132. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  133. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  134. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/require_typed_http_test_response.py +0 -0
  135. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  136. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/stepdown.py +0 -0
  137. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  138. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  139. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  140. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  141. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  142. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  143. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  144. {sarj_python_lint-0.87.7 → sarj_python_lint-0.87.9}/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.7
3
+ Version: 0.87.9
4
4
  Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
19
19
  Requires-Python: >=3.14
20
20
  Requires-Dist: packaging==26.3
21
21
  Requires-Dist: ruff>=0.16.8
22
- Requires-Dist: sqlglot==30.18.0
22
+ Requires-Dist: sqlglot==30.19.0
23
23
  Requires-Dist: typer==0.27.2
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "0.87.7"
3
+ version = "0.87.9"
4
4
  description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "sarj-ai" }]
@@ -15,7 +15,7 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3.14",
16
16
  "Topic :: Software Development :: Quality Assurance",
17
17
  ]
18
- dependencies = ["packaging==26.3", "ruff>=0.16.8", "sqlglot==30.18.0", "typer==0.27.2"]
18
+ dependencies = ["packaging==26.3", "ruff>=0.16.8", "sqlglot==30.19.0", "typer==0.27.2"]
19
19
 
20
20
  [project.scripts]
21
21
  sarj-python-lint = "sarj_python_lint.__main__:main"
@@ -36,7 +36,7 @@ dev = [
36
36
  ]
37
37
 
38
38
  [build-system]
39
- requires = ["hatchling==1.32.0"]
39
+ requires = ["hatchling==1.32.4"]
40
40
  build-backend = "hatchling.build"
41
41
 
42
42
  [tool.hatch.build.targets.wheel]
@@ -75,7 +75,7 @@ class NoSecretInLog(Rule):
75
75
  remediation="Omit the credential or log only approved non-sensitive metadata through a centralized sanitizer.",
76
76
  category=RuleCategory.SECURITY,
77
77
  limitations=(
78
- "Detection covers direct secret-named names, terminal attributes, constant secret-key subscripts, identity slices, f-string and literal format arguments, literal extra mappings, and immediate structured-logger bindings.",
78
+ "Detection covers direct secret-named names, terminal attributes, constant secret-key subscripts, identity slices, f-string and literal format arguments, nested literal extra mappings, literal keyword unpacking, and immediate structured-logger bindings.",
79
79
  "Logger recognition and secret classification are lexical; general aliases, arbitrary calls, dynamic containers, and interprocedural dataflow are not inspected.",
80
80
  ),
81
81
  examples=(
@@ -176,14 +176,43 @@ def _unsafe_formatted_values(parts: list[ast.expr]) -> tuple[ast.expr, ...]:
176
176
 
177
177
  def _collect_keyword_secrets(path: Path, node: ast.Call, code: str, diags: list[Diagnostic]) -> None:
178
178
  for kw in node.keywords:
179
- # `**kwargs` has arg=None — nothing to inspect.
180
179
  if kw.arg is None:
180
+ if isinstance(kw.value, ast.Dict):
181
+ diags.extend(
182
+ _diagnostic(path, value, code, "literal unpacked logging field")
183
+ for value in _literal_mapping_secret_values(kw.value)
184
+ )
181
185
  continue
182
186
  if kw.arg == "extra" and isinstance(kw.value, ast.Dict):
183
187
  diags.extend(
184
188
  _diagnostic(path, value, code, "literal `extra` field")
185
- for value in kw.value.values
186
- if _is_raw_secret_reference(value)
189
+ for value in _literal_mapping_secret_values(kw.value)
187
190
  )
188
191
  elif _is_raw_secret_reference(kw.value):
189
192
  diags.append(_diagnostic(path, kw.value, code, f"`{kw.arg}` logging field"))
193
+
194
+
195
+ def _literal_mapping_secret_values(mapping: ast.Dict) -> tuple[ast.expr, ...]:
196
+ return tuple(
197
+ secret
198
+ for _, value in _literal_mapping_items(mapping)
199
+ for secret in (
200
+ _literal_mapping_secret_values(value)
201
+ if isinstance(value, ast.Dict)
202
+ else (value,)
203
+ if _is_raw_secret_reference(value)
204
+ else ()
205
+ )
206
+ )
207
+
208
+
209
+ def _literal_mapping_items(mapping: ast.Dict) -> tuple[tuple[str, ast.expr], ...]:
210
+ items: dict[str, ast.expr] = {}
211
+ for key, value in zip(mapping.keys, mapping.values, strict=True):
212
+ if key is None and isinstance(value, ast.Dict):
213
+ items.update(_literal_mapping_items(value))
214
+ elif isinstance(key, ast.Constant) and isinstance(key.value, str):
215
+ items[key.value] = value
216
+ else:
217
+ items.clear()
218
+ return tuple(items.items())