sarj-python-lint 0.87.3__tar.gz → 0.87.5__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 (143) hide show
  1. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/PKG-INFO +1 -1
  2. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/pyproject.toml +1 -1
  3. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/__main__.py +6 -11
  4. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_ratchet_cli.py +2 -2
  5. sarj_python_lint-0.87.5/src/sarj_python_lint/json_boundary.py +10 -0
  6. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/ratchet.py +6 -9
  7. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_ast_index.py +9 -15
  8. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +3 -3
  9. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +4 -3
  10. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +4 -4
  11. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/.gitignore +0 -0
  12. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/LICENSE +0 -0
  13. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/README.md +0 -0
  14. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/__init__.py +0 -0
  15. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_analysis_session.py +0 -0
  16. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_filesystem.py +0 -0
  17. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_python_target.py +0 -0
  18. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_secret_names.py +0 -0
  19. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/_version.py +0 -0
  20. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/py.typed +0 -0
  21. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rule_base.py +0 -0
  22. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/__init__.py +0 -0
  23. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  24. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  25. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_comments.py +0 -0
  26. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  27. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  28. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_first_party.py +0 -0
  29. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  30. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_imports.py +0 -0
  31. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_logging.py +0 -0
  32. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_paths.py +0 -0
  33. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_project_index.py +0 -0
  34. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  35. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_pytest.py +0 -0
  36. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_registry.py +0 -0
  37. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_sql.py +0 -0
  38. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  39. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  40. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  41. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  42. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  43. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  44. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  45. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  46. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  47. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  48. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  49. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  50. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  51. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  52. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  53. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  54. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  55. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  56. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  57. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  58. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  59. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  60. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  61. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  62. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  63. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  64. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  65. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  66. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  67. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  68. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  69. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  70. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  71. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  72. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  73. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  74. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  75. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  76. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  77. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  78. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  79. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  80. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  81. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  82. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  83. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  84. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  85. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  86. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  87. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  88. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  89. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  90. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  91. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  92. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  93. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  94. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  95. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  96. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  97. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  98. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  99. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  100. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  101. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  102. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  103. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  104. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  105. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  106. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  107. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  108. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  109. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  110. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  111. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  112. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  113. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  114. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  115. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  116. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  117. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  118. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  119. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  120. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  121. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  122. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  123. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  124. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  125. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  126. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  127. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  128. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  129. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  130. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  131. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  132. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  133. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/require_typed_http_test_response.py +0 -0
  134. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  135. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/stepdown.py +0 -0
  136. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  137. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  138. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  139. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  140. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  141. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  142. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  143. {sarj_python_lint-0.87.3 → sarj_python_lint-0.87.5}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sarj-python-lint
3
- Version: 0.87.3
3
+ Version: 0.87.5
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 = "0.87.3"
3
+ version = "0.87.5"
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" }]
@@ -14,6 +14,7 @@ import typer
14
14
  from sarj_python_lint import __version__
15
15
  from sarj_python_lint._analysis_session import AnalysisSession
16
16
  from sarj_python_lint._filesystem import atomic_write_text
17
+ from sarj_python_lint.json_boundary import is_object_mapping, parse_json
17
18
  from sarj_python_lint.rule_base import Diagnostic, ProjectRule, Rule, Severity, is_suppressed
18
19
  from sarj_python_lint.rules import REGISTRY
19
20
  from sarj_python_lint.rules._paths import clear_path_caches
@@ -335,20 +336,14 @@ def _baseline_path(path: Path, *, root: Path | None = None) -> str:
335
336
 
336
337
 
337
338
  def _read_baseline(path: Path) -> dict[str, dict[str, int]]:
338
- raw: object = json.loads( # pyright: ignore[reportAny] — json.loads is an untyped stdlib boundary; the shape is narrowed below
339
- path.read_text(encoding="utf-8")
340
- )
341
- if not isinstance(raw, dict):
339
+ raw = parse_json(path.read_text(encoding="utf-8"))
340
+ if not is_object_mapping(raw):
342
341
  return {}
343
342
  counts: dict[str, dict[str, int]] = {}
344
- for file_key, per_code in raw.items(): # pyright: ignore[reportUnknownVariableType] — json.loads yields Any leaves
345
- if not isinstance(file_key, str) or not isinstance(per_code, dict):
343
+ for file_key, per_code in raw.items():
344
+ if not isinstance(file_key, str) or not is_object_mapping(per_code):
346
345
  continue
347
- counts[file_key] = {
348
- code: n
349
- for code, n in per_code.items() # pyright: ignore[reportUnknownVariableType] — same
350
- if isinstance(code, str) and isinstance(n, int)
351
- }
346
+ counts[file_key] = {code: n for code, n in per_code.items() if isinstance(code, str) and isinstance(n, int)}
352
347
  return counts
353
348
 
354
349
 
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- import json
5
4
  from pathlib import Path
6
5
  import subprocess # ruff: ignore[suspicious-subprocess-import] -- fixed local Ruff introspection only
7
6
  import sys
@@ -11,6 +10,7 @@ import typer
11
10
 
12
11
  from sarj_python_lint._filesystem import atomic_write_text
13
12
  from sarj_python_lint._version import __version__
13
+ from sarj_python_lint.json_boundary import parse_json
14
14
  from sarj_python_lint.ratchet import (
15
15
  DEFAULT_PER_FILE_CEILING,
16
16
  Baseline,
@@ -142,7 +142,7 @@ def _ruff_selector_aliases() -> dict[str, str]:
142
142
  encoding="utf-8",
143
143
  text=True,
144
144
  )
145
- raw: object = json.loads(completed.stdout) # pyright: ignore[reportAny] -- validated below
145
+ raw = parse_json(completed.stdout)
146
146
  if not _is_object_list(raw):
147
147
  msg = "Ruff rule catalog was not a JSON list"
148
148
  raise TypeError(msg)
@@ -0,0 +1,10 @@
1
+ import json
2
+ from typing import TypeIs
3
+
4
+
5
+ def parse_json(text: str) -> object:
6
+ return json.loads(text) # pyright: ignore[reportAny] -- untyped stdlib parser boundary.
7
+
8
+
9
+ def is_object_mapping(value: object) -> TypeIs[dict[object, object]]:
10
+ return isinstance(value, dict)
@@ -9,6 +9,8 @@ import tokenize
9
9
  from types import MappingProxyType
10
10
  from typing import TYPE_CHECKING, Final, NamedTuple, TypeGuard
11
11
 
12
+ from sarj_python_lint.json_boundary import is_object_mapping, parse_json
13
+
12
14
 
13
15
  if TYPE_CHECKING:
14
16
  from collections.abc import Iterable, Iterator, Mapping
@@ -216,9 +218,7 @@ def seed(measurement: Measurement, baseline: Baseline) -> Baseline:
216
218
 
217
219
 
218
220
  def load_baseline(path: Path) -> Baseline:
219
- raw: object = json.loads( # pyright: ignore[reportAny] — json.loads is an untyped stdlib boundary; every read below narrows
220
- path.read_text(encoding="utf-8")
221
- )
221
+ raw = parse_json(path.read_text(encoding="utf-8"))
222
222
  schema = _get(raw, "schema_version")
223
223
  if schema is not None and schema != BASELINE_SCHEMA_VERSION:
224
224
  msg = f"unsupported suppression baseline schema_version: {schema!r}"
@@ -238,9 +238,9 @@ def load_baseline(path: Path) -> Baseline:
238
238
 
239
239
 
240
240
  def _get(mapping: object, key: str) -> object:
241
- if not isinstance(mapping, dict):
241
+ if not is_object_mapping(mapping):
242
242
  return None
243
- return mapping.get(key) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] — json leaves are Any
243
+ return mapping.get(key)
244
244
 
245
245
 
246
246
  def dump_baseline(baseline: Baseline, packages: Iterable[str]) -> str:
@@ -308,10 +308,7 @@ def _is_object_list(value: object) -> TypeGuard[list[object]]:
308
308
 
309
309
 
310
310
  def _is_string_object_mapping(value: object) -> TypeGuard[dict[str, object]]:
311
- return isinstance(value, dict) and all(
312
- isinstance(key, str)
313
- for key in value # pyright: ignore[reportUnknownVariableType] — runtime validation narrows untyped JSON keys.
314
- )
311
+ return is_object_mapping(value) and all(isinstance(key, str) for key in value)
315
312
 
316
313
 
317
314
  def _normalized_subtree(value: str) -> str:
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import ast
6
- from typing import TYPE_CHECKING, final
6
+ from typing import TYPE_CHECKING, TypeIs, final
7
7
 
8
8
 
9
9
  if TYPE_CHECKING:
@@ -13,12 +13,16 @@ if TYPE_CHECKING:
13
13
  _AST = ast.AST
14
14
 
15
15
 
16
+ def object_list(value: object) -> TypeIs[list[object]]:
17
+ return isinstance(value, list)
18
+
19
+
16
20
  def children(node: ast.AST) -> list[ast.AST]:
17
21
  out: list[ast.AST] = []
18
22
  for name in node._fields:
19
23
  value: object = getattr(node, name, None)
20
- if isinstance(value, list):
21
- out += [item for item in value if isinstance(item, _AST)] # pyright: ignore[reportUnknownVariableType] — element narrowed by isinstance
24
+ if object_list(value):
25
+ out += [item for item in value if isinstance(item, _AST)]
22
26
  elif isinstance(value, _AST):
23
27
  out.append(value)
24
28
  return out
@@ -30,12 +34,7 @@ def walk(node: ast.AST) -> Iterator[ast.AST]:
30
34
  while i < len(queue):
31
35
  current = queue[i]
32
36
  i += 1
33
- for name in current._fields:
34
- value: object = getattr(current, name, None)
35
- if isinstance(value, list):
36
- queue += [item for item in value if isinstance(item, _AST)] # pyright: ignore[reportUnknownVariableType] — element narrowed by isinstance
37
- elif isinstance(value, _AST):
38
- queue.append(value)
37
+ queue += children(current)
39
38
  yield current
40
39
 
41
40
 
@@ -56,12 +55,7 @@ class _NodeIndex:
56
55
  buckets[cls] = [current]
57
56
  else:
58
57
  bucket.append(current)
59
- for name in cls._fields:
60
- value: object = getattr(current, name, None)
61
- if isinstance(value, list):
62
- flat += [item for item in value if isinstance(item, _AST)] # pyright: ignore[reportUnknownVariableType] — element narrowed by isinstance
63
- elif isinstance(value, _AST):
64
- flat.append(value)
58
+ flat += children(current)
65
59
  self._buckets: dict[type[ast.AST], list[ast.AST]] = buckets
66
60
  self._flat: list[ast.AST] = flat
67
61
  self._queries: dict[tuple[type[ast.AST], ...], list[ast.AST]] = {}
@@ -19,7 +19,7 @@ from sarj_python_lint.rule_base import (
19
19
  Severity,
20
20
  parse_or_none,
21
21
  )
22
- from sarj_python_lint.rules._ast_index import nodes
22
+ from sarj_python_lint.rules._ast_index import nodes, object_list
23
23
  from sarj_python_lint.rules._logging import LOG_METHODS, is_logger_expr
24
24
 
25
25
 
@@ -308,9 +308,9 @@ def _analysis_facts(tree: ast.Module) -> _AnalysisFacts:
308
308
 
309
309
 
310
310
  def _statement_list(value: object) -> list[ast.stmt] | None:
311
- if not isinstance(value, list):
311
+ if not object_list(value):
312
312
  return None
313
- ast_items: list[ast.AST] = [item for item in value if isinstance(item, ast.AST)] # pyright: ignore[reportUnknownVariableType] — narrowed by isinstance
313
+ ast_items: list[ast.AST] = [item for item in value if isinstance(item, ast.AST)]
314
314
  if not ast_items or not all(isinstance(item, ast.stmt) for item in ast_items):
315
315
  return None
316
316
  return [item for item in ast_items if isinstance(item, ast.stmt)]
@@ -20,6 +20,7 @@ from sarj_python_lint.rule_base import (
20
20
  is_suppressed,
21
21
  parse_or_none,
22
22
  )
23
+ from sarj_python_lint.rules._ast_index import object_list
23
24
  from sarj_python_lint.rules._paths import is_generated
24
25
 
25
26
 
@@ -314,11 +315,11 @@ def _statement_lists(tree: ast.AST) -> list[list[ast.stmt]]:
314
315
  result: list[list[ast.stmt]] = []
315
316
  for node in ast.walk(tree):
316
317
  for field in ("body", "orelse", "finalbody"):
317
- value = getattr(node, field, None)
318
- if not isinstance(value, list):
318
+ value: object = getattr(node, field, None)
319
+ if not object_list(value):
319
320
  continue
320
321
  statements: list[ast.stmt] = []
321
- for item in value: # pyright: ignore[reportUnknownVariableType] -- AST fields are untyped lists
322
+ for item in value:
322
323
  if not isinstance(item, ast.stmt):
323
324
  break
324
325
  statements.append(item)
@@ -18,7 +18,7 @@ from sarj_python_lint.rule_base import (
18
18
  is_suppressed,
19
19
  parse_or_none,
20
20
  )
21
- from sarj_python_lint.rules._ast_index import children, nodes, walk
21
+ from sarj_python_lint.rules._ast_index import children, nodes, object_list, walk
22
22
  from sarj_python_lint.rules._paths import is_generated, is_test_path
23
23
 
24
24
 
@@ -148,10 +148,10 @@ class PreferWalrusRegexMatch(Rule):
148
148
  )
149
149
 
150
150
  for node in walk(tree):
151
- raw_body = getattr(node, "body", None)
152
- if not isinstance(raw_body, list):
151
+ raw_body: object = getattr(node, "body", None)
152
+ if not object_list(raw_body):
153
153
  continue
154
- body: list[ast.stmt] = [st for st in raw_body if isinstance(st, ast.stmt)] # pyright: ignore[reportUnknownVariableType]
154
+ body: list[ast.stmt] = [st for st in raw_body if isinstance(st, ast.stmt)]
155
155
  diags.extend(_check_body(node, body, context))
156
156
 
157
157
  return sorted(diags, key=lambda d: (d.line, d.col))