sarj-python-lint 1.104.0__tar.gz → 1.108.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.
Files changed (319) hide show
  1. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/PKG-INFO +2 -2
  2. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/pyproject.toml +2 -2
  3. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/__init__.py +14 -3
  4. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/__main__.py +1 -1
  5. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix.py +6 -2
  6. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_fixes.py +3 -1
  7. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_rule_descriptors.py +28 -1
  8. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rule_metadata.py +16 -3
  9. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_ast_index.py +5 -1
  10. sarj_python_lint-1.108.0/src/sarj_python_lint/rules/_ast_position.py +25 -0
  11. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_nominal_project.py +7 -1
  12. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_registry.py +6 -0
  13. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_resource_provenance.py +8 -1
  14. sarj_python_lint-1.108.0/src/sarj_python_lint/rules/_runtime_names.py +258 -0
  15. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_runtime_registry.py +10 -3
  16. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_store_writes.py +4 -8
  17. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +1 -1
  18. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +1 -1
  19. sarj_python_lint-1.108.0/src/sarj_python_lint/rules/no_dynamic_builtins.py +100 -0
  20. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_hasattr.py +9 -41
  21. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +9 -2
  22. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +9 -2
  23. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_redundant_local_alias.py +3 -1
  24. sarj_python_lint-1.108.0/src/sarj_python_lint/rules/no_shared_mutable_default.py +215 -0
  25. sarj_python_lint-1.108.0/src/sarj_python_lint/rules/no_sys_path_mutation.py +142 -0
  26. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +5 -1
  27. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_class_row.py +11 -1
  28. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +6 -2
  29. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +3 -1
  30. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +10 -1
  31. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_str_enum.py +9 -2
  32. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +7 -2
  33. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +41 -8
  34. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +3 -1
  35. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +9 -2
  36. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +3 -1
  37. sarj_python_lint-1.104.0/src/sarj_python_lint/rules/_ast_position.py +0 -21
  38. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/.gitignore +0 -0
  39. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/LICENSE +0 -0
  40. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/README.md +0 -0
  41. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  42. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_aliases.py +0 -0
  43. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_annotations.py +0 -0
  44. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_comments.py +0 -0
  45. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
  46. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
  47. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_docs.py +0 -0
  48. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_enums.py +0 -0
  49. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
  50. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_inert.py +0 -0
  51. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_parameters.py +0 -0
  52. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_repeated.py +0 -0
  53. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_security.py +0 -0
  54. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
  55. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_autofix_tests.py +0 -0
  56. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_file_context.py +0 -0
  57. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_filesystem.py +0 -0
  58. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_parallel.py +0 -0
  59. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_prepared_sources.py +0 -0
  60. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_python_target.py +0 -0
  61. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  62. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_secret_names.py +0 -0
  63. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_source.py +0 -0
  64. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/_version.py +0 -0
  65. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/application_logging.py +0 -0
  66. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/autofix.py +0 -0
  67. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/benchmark.py +0 -0
  68. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/cast_access.py +0 -0
  69. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
  70. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/json_boundary.py +0 -0
  71. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/py.typed +0 -0
  72. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/ratchet.py +0 -0
  73. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rule_base.py +0 -0
  74. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/__init__.py +0 -0
  75. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
  76. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  77. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_ast_names.py +0 -0
  78. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
  79. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_binding_inventory.py +0 -0
  80. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  81. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
  82. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  83. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  84. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  85. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
  86. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
  87. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  88. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  89. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_exports.py +0 -0
  90. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  91. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  92. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  93. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_imports.py +0 -0
  94. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  95. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  96. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
  97. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
  98. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  99. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
  100. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  101. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  102. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_native_object_contracts.py +0 -0
  103. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
  104. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
  105. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  106. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_postgres.py +0 -0
  107. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_postgres_ctas.py +0 -0
  108. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
  109. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  110. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  111. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  112. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  113. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
  114. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  115. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
  116. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  117. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  118. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
  119. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
  120. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
  121. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  122. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_store_sql.py +0 -0
  123. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  124. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  125. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  126. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  127. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
  128. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  129. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  130. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
  131. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  132. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
  133. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
  134. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  135. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  136. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
  137. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  138. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  139. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  140. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  141. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
  142. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  143. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
  144. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  145. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  146. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  147. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  148. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  149. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
  150. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  151. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  152. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  153. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_async_client_in_constructor.py +0 -0
  154. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  155. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  156. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
  157. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  158. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  159. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  160. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  161. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  162. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  163. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  164. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
  165. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_custom_control_flow_exception.py +0 -0
  166. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  167. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
  168. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  169. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_discarded_tagged_outcome.py +0 -0
  170. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  171. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
  172. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  173. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  174. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
  175. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
  176. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  177. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  178. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  179. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  180. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  181. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  182. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
  183. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  184. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_generic_json_parser.py +0 -0
  185. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  186. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  187. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  188. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  189. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  190. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  191. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  192. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
  193. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  194. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  195. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  196. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  197. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  198. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_mutating_input_with_result.py +0 -0
  199. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  200. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  201. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  202. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  203. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  204. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  205. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  206. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_recording_store.py +0 -0
  207. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_redundant_attribute_unpacking.py +0 -0
  208. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  209. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  210. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_redundant_sql_alias.py +0 -0
  211. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  212. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  213. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  214. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  215. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  216. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  217. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  218. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  219. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
  220. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  221. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  222. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  223. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_test_comments.py +0 -0
  224. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  225. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  226. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  227. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  228. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  229. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  230. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  231. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  232. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  233. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
  234. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  235. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  236. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  237. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_application_model_joins.py +0 -0
  238. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  239. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  240. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  241. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  242. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
  243. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
  244. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
  245. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  246. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
  247. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  248. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  249. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  250. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  251. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  252. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  253. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  254. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  255. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  256. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  257. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  258. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  259. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  260. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_optional_composite.py +0 -0
  261. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
  262. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  263. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  264. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
  265. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
  266. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_contract.py +0 -0
  267. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  268. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  269. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_runtime_validation.py +0 -0
  270. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  271. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  272. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  273. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_typed_cli.py +0 -0
  274. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
  275. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  276. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  277. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  278. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
  279. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  280. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  281. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  282. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  283. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  284. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  285. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  286. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  287. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_constrained_api_integer.py +0 -0
  288. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
  289. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  290. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  291. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  292. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  293. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  294. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  295. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  296. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  297. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  298. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  299. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
  300. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  301. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  302. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  303. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/service_singleton_delegates_to_bulk.py +0 -0
  304. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  305. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
  306. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  307. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  308. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/store_insert_requires_explicit_conflict_policy.py +0 -0
  309. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  310. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/store_mutation_command_fanout.py +0 -0
  311. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  312. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  313. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  314. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  315. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  316. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  317. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  318. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
  319. {sarj_python_lint-1.104.0 → sarj_python_lint-1.108.0}/src/sarj_python_lint/suppression_policy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sarj-python-lint
3
- Version: 1.104.0
3
+ Version: 1.108.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/
@@ -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.17.2
22
- Requires-Dist: sarj-rule-contracts==0.6.0
22
+ Requires-Dist: sarj-rule-contracts==0.6.1
23
23
  Requires-Dist: sqlglot==30.24.0
24
24
  Requires-Dist: typer==0.27.3
25
25
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.104.0"
3
+ version = "1.108.0"
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 = ["sarj-rule-contracts==0.6.0", "packaging==26.3", "ruff>=0.17.2", "sqlglot==30.24.0", "typer==0.27.3"]
18
+ dependencies = ["sarj-rule-contracts==0.6.1", "packaging==26.3", "ruff>=0.17.2", "sqlglot==30.24.0", "typer==0.27.3"]
19
19
 
20
20
  [project.scripts]
21
21
  sarj-python-lint = "sarj_python_lint.__main__:main"
@@ -20,13 +20,24 @@ def __getattr__(name: str) -> object:
20
20
  raise AttributeError(msg)
21
21
  from sarj_python_lint._analysis_session import AnalysisSession as _AnalysisSession # ruff: ignore[import-outside-top-level] -- lightweight metadata imports do not need semantic rules.
22
22
 
23
- globals()["AnalysisSession"] = _AnalysisSession
23
+ globals( # sarj-noqa: SARJ557 -- Preserve the lazy public module namespace and historical star-import exports.
24
+ )["AnalysisSession"] = _AnalysisSession
24
25
  if name == "__all__":
25
26
  # Star imports previously exposed the complete post-session public namespace.
26
27
  # Discover it on demand instead of freezing a narrower list of exports.
27
- return tuple(public for public in globals() if not public.startswith("_"))
28
+ return tuple(
29
+ public
30
+ for public in globals( # sarj-noqa: SARJ557 -- Preserve the lazy public module namespace and historical star-import exports.
31
+ )
32
+ if not public.startswith("_")
33
+ )
28
34
  return _AnalysisSession
29
35
 
30
36
 
31
37
  def __dir__() -> list[str]:
32
- return sorted(set(globals()).union({"AnalysisSession"}))
38
+ return sorted(
39
+ set(
40
+ globals( # sarj-noqa: SARJ557 -- Preserve the lazy public module namespace and historical star-import exports.
41
+ )
42
+ ).union({"AnalysisSession"})
43
+ )
@@ -103,7 +103,7 @@ def _python_files(p: Path) -> list[Path]:
103
103
  for directory, directories, filenames in p.walk():
104
104
  directories[:] = [name for name in directories if name not in SKIP_DIR_NAMES]
105
105
  for name in filenames:
106
- if not fnmatch(name, "*.py"):
106
+ if not fnmatch(name, "*.py") and not fnmatch(name, "*.pyi"):
107
107
  continue
108
108
  child = directory / name
109
109
  if child.is_file():
@@ -514,7 +514,9 @@ def _repeated_fix(source: _Source, diagnostic: Diagnostic) -> list[TextEdit]:
514
514
  def _statement_pairs(context: PythonFileContext) -> Iterator[tuple[ast.stmt, ast.stmt]]:
515
515
  for owner in context.nodes(ast.AST):
516
516
  for field in ("body", "orelse", "finalbody"):
517
- value: object = getattr(owner, field, None)
517
+ value: object = getattr( # sarj-noqa: SARJ557 -- Inspect optional statement-list fields across heterogeneous AST node types.
518
+ owner, field, None
519
+ )
518
520
  if object_list(value):
519
521
  statements = [item for item in value if isinstance(item, ast.stmt)]
520
522
  yield from pairwise(statements)
@@ -1399,7 +1401,9 @@ def fix_source(path: Path, source: str, *, rule_ids: Sequence[str] | None = None
1399
1401
  # Batch independent candidates; overlapping candidates get fresh offsets on the next pass.
1400
1402
  selected_pass = _coherent_edits(candidates)
1401
1403
  updated = apply_edits(current, selected_pass.edits)
1402
- compile(updated, str(path), "exec", dont_inherit=True)
1404
+ compile( # sarj-noqa: SARJ557 -- Compile proposed fixes to reject invalid scopes without executing them.
1405
+ updated, str(path), "exec", dont_inherit=True
1406
+ )
1403
1407
  if updated == current:
1404
1408
  return current
1405
1409
  if passes == _MAX_FIX_PASSES:
@@ -65,5 +65,7 @@ def apply_fixes(source: str, fixes: Sequence[Fix]) -> str:
65
65
  result.append(raw[previous:])
66
66
  fixed = b"".join(result).decode("utf-8")
67
67
  tree = ast.parse(fixed)
68
- compile(tree, "<native-fix>", "exec")
68
+ compile( # sarj-noqa: SARJ557 -- Reject compiler-invalid fixes without executing the generated code object.
69
+ tree, "<native-fix>", "exec"
70
+ )
69
71
  return prefix + fixed
@@ -8,6 +8,33 @@ from sarj_python_lint.rule_metadata import RuleDescriptor
8
8
 
9
9
  SCHEMA_VERSION = 1
10
10
  DESCRIPTORS: tuple[RuleDescriptor, ...] = (
11
+ RuleDescriptor(
12
+ rule_id=RuleId("no-shared-mutable-default"),
13
+ module="sarj_python_lint.rules.no_shared_mutable_default",
14
+ class_name="NoSharedMutableDefault",
15
+ code="SARJ559",
16
+ description="Do not share mutable function defaults through aliases or read-only annotations.",
17
+ aliases=(),
18
+ enabled_by_default=True,
19
+ ),
20
+ RuleDescriptor(
21
+ rule_id=RuleId("no-sys-path-mutation"),
22
+ module="sarj_python_lint.rules.no_sys_path_mutation",
23
+ class_name="NoSysPathMutation",
24
+ code="SARJ558",
25
+ description="Do not mutate the interpreter import search path.",
26
+ aliases=(),
27
+ enabled_by_default=True,
28
+ ),
29
+ RuleDescriptor(
30
+ rule_id=RuleId("no-dynamic-builtins"),
31
+ module="sarj_python_lint.rules.no_dynamic_builtins",
32
+ class_name="NoDynamicBuiltins",
33
+ code="SARJ557",
34
+ description="Use explicit typed state instead of dynamic Python builtin access.",
35
+ aliases=(),
36
+ enabled_by_default=True,
37
+ ),
11
38
  RuleDescriptor(
12
39
  rule_id=RuleId("service-singleton-delegates-to-bulk"),
13
40
  module="sarj_python_lint.rules.service_singleton_delegates_to_bulk",
@@ -337,7 +364,7 @@ DESCRIPTORS: tuple[RuleDescriptor, ...] = (
337
364
  module="sarj_python_lint.rules.no_hasattr",
338
365
  class_name="NoHasattr",
339
366
  code="SARJ499",
340
- description="Production code should not probe attributes with hasattr.",
367
+ description="Do not probe attributes with the builtin hasattr.",
341
368
  aliases=(),
342
369
  enabled_by_default=True,
343
370
  ),
@@ -53,11 +53,19 @@ def is_alias_tuple(value: object) -> TypeIs[tuple[str, ...]]:
53
53
 
54
54
  def load_descriptors() -> tuple[RuleDescriptor, ...]:
55
55
  module = import_module("sarj_python_lint._rule_descriptors")
56
- version: object = getattr(module, "SCHEMA_VERSION", None)
56
+ version: object = (
57
+ getattr( # sarj-noqa: SARJ557 -- Validate imported plugin metadata before trusting its declared rule contract.
58
+ module, "SCHEMA_VERSION", None
59
+ )
60
+ )
57
61
  if type(version) is not int or version != DESCRIPTOR_SCHEMA_VERSION:
58
62
  msg = "unsupported Python rule descriptor schema"
59
63
  raise ValueError(msg)
60
- value: object = getattr(module, "DESCRIPTORS", None)
64
+ value: object = (
65
+ getattr( # sarj-noqa: SARJ557 -- Validate imported plugin metadata before trusting its declared rule contract.
66
+ module, "DESCRIPTORS", None
67
+ )
68
+ )
61
69
  if not _object_tuple(value):
62
70
  msg = "Python rule descriptors must be a tuple"
63
71
  raise TypeError(msg)
@@ -102,7 +110,12 @@ def _authored_descriptor(rule_id: RuleId, rule: type[Rule]) -> RuleDescriptor:
102
110
  msg = f"{rule_id}: instance metadata differs from class metadata"
103
111
  raise ValueError(msg)
104
112
  module = import_module(rule.__module__)
105
- if getattr(module, rule.__name__, None) is not rule:
113
+ if (
114
+ getattr( # sarj-noqa: SARJ557 -- Validate imported plugin metadata before trusting its declared rule contract.
115
+ module, rule.__name__, None
116
+ )
117
+ is not rule
118
+ ):
106
119
  msg = f"{rule_id}: rule class is not importable from its declared module"
107
120
  raise ValueError(msg)
108
121
  return RuleDescriptor(
@@ -47,7 +47,11 @@ def object_list(value: object) -> TypeIs[list[object]]: # sarj-noqa: SARJ528 --
47
47
  def children(node: ast.AST) -> list[ast.AST]:
48
48
  out: list[ast.AST] = []
49
49
  for name in node._fields:
50
- value: object = getattr(node, name, None)
50
+ value: object = (
51
+ getattr( # sarj-noqa: SARJ557 -- Preserve the profiled AST child traversal and missing-field behavior.
52
+ node, name, None
53
+ )
54
+ )
51
55
  if object_list(value):
52
56
  out += [item for item in value if isinstance(item, _AST)]
53
57
  elif isinstance(value, _AST):
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
5
+
6
+
7
+ if TYPE_CHECKING:
8
+ import ast
9
+
10
+
11
+ @dataclass(frozen=True, order=True, slots=True)
12
+ class AstPosition:
13
+ line: int
14
+ column: int
15
+
16
+
17
+ def ast_position(node: ast.AST, *, missing: int) -> AstPosition:
18
+ return AstPosition(
19
+ line=getattr( # sarj-noqa: SARJ557 -- Generic AST markers may omit source locations; preserve the caller fallback.
20
+ node, "lineno", missing
21
+ ),
22
+ column=getattr( # sarj-noqa: SARJ557 -- Generic AST markers may omit source locations; preserve the caller fallback.
23
+ node, "col_offset", missing
24
+ ),
25
+ )
@@ -19,6 +19,8 @@ from sarj_python_lint.rules._paths import is_generated
19
19
 
20
20
 
21
21
  if TYPE_CHECKING:
22
+ from typing import Never
23
+
22
24
  from sarj_python_lint.rules._first_party import FirstPartyFacts
23
25
 
24
26
 
@@ -239,10 +241,14 @@ def _patterns(value: object) -> tuple[str, ...] | None:
239
241
  return tuple(item for item in value if isinstance(item, str))
240
242
 
241
243
 
244
+ def _raise_walk_error(error: OSError) -> Never:
245
+ raise error
246
+
247
+
242
248
  def _files(root: Path, pattern: str) -> list[Path]:
243
249
  found: list[Path] = []
244
250
  try:
245
- for count, (directory, directories, filenames) in enumerate(root.walk()):
251
+ for count, (directory, directories, filenames) in enumerate(root.walk(on_error=_raise_walk_error)):
246
252
  if count >= _MAX_DIRECTORIES or len(found) >= _MAX_FILES:
247
253
  # A partial inventory cannot establish uniqueness.
248
254
  return []
@@ -240,6 +240,7 @@ from sarj_python_lint.rules.no_custom_control_flow_exception import NoCustomCont
240
240
  from sarj_python_lint.rules.no_defaulted_environment_setting import NoDefaultedEnvironmentSetting
241
241
  from sarj_python_lint.rules.no_discarded_tagged_outcome import NoDiscardedTaggedOutcome
242
242
  from sarj_python_lint.rules.no_duplicate_field_validator_body import NoDuplicateFieldValidatorBody
243
+ from sarj_python_lint.rules.no_dynamic_builtins import NoDynamicBuiltins
243
244
  from sarj_python_lint.rules.no_exception_text_in_500_detail import NoExceptionTextIn500Detail
244
245
  from sarj_python_lint.rules.no_folded_bad_request_not_found import NoFoldedBadRequestNotFound
245
246
  from sarj_python_lint.rules.no_hasattr import NoHasattr
@@ -251,7 +252,9 @@ from sarj_python_lint.rules.no_recording_store import NoRecordingStore
251
252
  from sarj_python_lint.rules.no_redundant_local_alias import NoRedundantLocalAlias
252
253
  from sarj_python_lint.rules.no_redundant_sql_alias import NoRedundantSqlAlias
253
254
  from sarj_python_lint.rules.no_shallow_record_type_guard import NoShallowRecordTypeGuard
255
+ from sarj_python_lint.rules.no_shared_mutable_default import NoSharedMutableDefault
254
256
  from sarj_python_lint.rules.no_slash_collection_root_under_prefix import NoSlashCollectionRootUnderPrefix
257
+ from sarj_python_lint.rules.no_sys_path_mutation import NoSysPathMutation
255
258
  from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
256
259
  from sarj_python_lint.rules.no_work_verb_get_route import NoWorkVerbGetRoute
257
260
  from sarj_python_lint.rules.prefer_application_model_joins import PreferApplicationModelJoins
@@ -283,6 +286,9 @@ from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOr
283
286
 
284
287
  _AUTHORING_REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
285
288
  {
289
+ NoSharedMutableDefault.id: NoSharedMutableDefault,
290
+ NoSysPathMutation.id: NoSysPathMutation,
291
+ NoDynamicBuiltins.id: NoDynamicBuiltins,
286
292
  ServiceSingletonDelegatesToBulk.id: ServiceSingletonDelegatesToBulk,
287
293
  NoAsyncClientInConstructor.id: NoAsyncClientInConstructor,
288
294
  NoDiscardedTaggedOutcome.id: NoDiscardedTaggedOutcome,
@@ -338,7 +338,14 @@ class ResourceProvenance:
338
338
  return None
339
339
  if not isinstance(self.parents.get(statement), ast.Module | ast.FunctionDef | ast.AsyncFunctionDef):
340
340
  return None
341
- return statement.value if statement.lineno < getattr(at, "lineno", 0) else None
341
+ return (
342
+ statement.value
343
+ if statement.lineno
344
+ < getattr( # sarj-noqa: SARJ557 -- Order heterogeneous AST markers while preserving the missing-location fallback.
345
+ at, "lineno", 0
346
+ )
347
+ else None
348
+ )
342
349
 
343
350
  def async_function(self, value: ast.expr, at: ast.AST) -> ast.AsyncFunctionDef | None:
344
351
  if self.dynamic_namespace:
@@ -0,0 +1,258 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from dataclasses import dataclass
5
+ import keyword
6
+ from typing import TYPE_CHECKING, final
7
+
8
+
9
+ if TYPE_CHECKING:
10
+ from collections.abc import Iterable
11
+
12
+ from sarj_python_lint._file_context import PythonFileContext
13
+
14
+
15
+ _FUNCTION_SCOPES = (
16
+ ast.FunctionDef,
17
+ ast.AsyncFunctionDef,
18
+ ast.Lambda,
19
+ ast.ListComp,
20
+ ast.SetComp,
21
+ ast.DictComp,
22
+ ast.GeneratorExp,
23
+ )
24
+ _NATIVE_NAMES = frozenset(
25
+ {
26
+ "__import__",
27
+ "bool",
28
+ "bytearray",
29
+ "bytes",
30
+ "complex",
31
+ "compile",
32
+ "delattr",
33
+ "dict",
34
+ "float",
35
+ "frozenset",
36
+ "getattr",
37
+ "globals",
38
+ "hasattr",
39
+ "int",
40
+ "list",
41
+ "locals",
42
+ "object",
43
+ "range",
44
+ "set",
45
+ "setattr",
46
+ "str",
47
+ "tuple",
48
+ }
49
+ )
50
+
51
+ _RUFF_BANNED_API = frozenset({"locals", "globals", "compile", "__import__", "getattr", "hasattr", "delattr"})
52
+
53
+
54
+ @dataclass(frozen=True, slots=True)
55
+ class BuiltinReference:
56
+ node: ast.expr | ast.alias
57
+ name: str
58
+
59
+
60
+ @final
61
+ class RuntimeNames:
62
+ def __init__(self, context: PythonFileContext) -> None:
63
+ self.context = context
64
+ self.inventory = context.binding_inventory
65
+
66
+ def builtin(self, node: ast.Name) -> bool:
67
+ return not self.inventory.wildcard and self._binding_scope(node.id, node) is None
68
+
69
+ def qualified(self, expression: ast.expr) -> str | None:
70
+ if self.inventory.wildcard:
71
+ return None
72
+ return self._qualified(expression, frozenset(), assignments=True)
73
+
74
+ def imported_qualified(self, expression: ast.expr) -> str | None:
75
+ if self.inventory.wildcard:
76
+ return None
77
+ return self._qualified(expression, frozenset(), assignments=False)
78
+
79
+ def assigned_value(self, node: ast.Name) -> ast.expr | None:
80
+ scope = self._binding_scope(node.id, node)
81
+ declarations = self.inventory.bindings.get(scope, {}).get(node.id, ())
82
+ stable = tuple(item for item in declarations if not self._inplace_target(item))
83
+ if len(stable) != 1 or not isinstance(stable[0], ast.Name):
84
+ return None
85
+ return self._assignment(stable[0])
86
+
87
+ def banned_references(self, names: frozenset[str]) -> list[BuiltinReference]:
88
+ references = [
89
+ reference
90
+ for node in self.context.nodes(ast.Name, ast.Attribute)
91
+ if (reference := self._banned_reference(node, names)) is not None
92
+ ]
93
+ if imported := names.intersection({"exec", "eval"}):
94
+ references.extend(
95
+ BuiltinReference(alias, alias.name)
96
+ for statement in self.context.nodes(ast.ImportFrom)
97
+ if statement.level == 0 and statement.module == "builtins"
98
+ for alias in statement.names
99
+ if alias.name in imported
100
+ )
101
+ return sorted(references, key=lambda item: (item.node.lineno, item.node.col_offset))
102
+
103
+ def _banned_reference(self, node: ast.Name | ast.Attribute, names: frozenset[str]) -> BuiltinReference | None:
104
+ if not isinstance(node.ctx, ast.Load):
105
+ return None
106
+ if isinstance(node, ast.Name):
107
+ if node.id not in names or not self.builtin(node):
108
+ return None
109
+ name = node.id
110
+ direct_import = False
111
+ else:
112
+ qualified = self.qualified(node)
113
+ if qualified is None or not qualified.startswith("builtins."):
114
+ return None
115
+ name = qualified.removeprefix("builtins.")
116
+ if name not in names:
117
+ return None
118
+ direct_import = self._direct_builtin_module(node.value)
119
+ if self._native_owner(node, name, direct_import=direct_import):
120
+ return None
121
+ return BuiltinReference(node, name)
122
+
123
+ def _direct_builtin_module(self, node: ast.expr) -> bool:
124
+ if not isinstance(node, ast.Name):
125
+ return False
126
+ scope = self._binding_scope(node.id, node)
127
+ declarations = self.inventory.bindings.get(scope, {}).get(node.id, ())
128
+ return bool(declarations) and all(
129
+ isinstance(item, ast.alias)
130
+ and isinstance(self.context.parents.get(item), ast.Import)
131
+ and item.name == "builtins"
132
+ for item in declarations
133
+ )
134
+
135
+ def _native_owner(self, node: ast.expr, name: str, *, direct_import: bool) -> bool:
136
+ if direct_import and name in _RUFF_BANNED_API:
137
+ return True
138
+ if name == "eval" and (isinstance(node, ast.Name) or direct_import):
139
+ return True
140
+ parent = self.context.parents.get(node)
141
+ if not isinstance(parent, ast.Call) or parent.func is not node:
142
+ return False
143
+ if name in {"exec", "breakpoint"}:
144
+ return isinstance(node, ast.Name) or direct_import
145
+ if isinstance(node, ast.Attribute) or parent.keywords:
146
+ return False
147
+ match parent.args:
148
+ case [receiver, ast.Constant(value=str() as value)]:
149
+ if name == "getattr" and isinstance(receiver, ast.Starred):
150
+ return False
151
+ case _:
152
+ return False
153
+ if name == "hasattr":
154
+ return value == "__call__"
155
+ if name != "getattr":
156
+ return False
157
+ mangled = value.startswith("__") and not value.endswith("__")
158
+ return value.isidentifier() and not keyword.iskeyword(value) and not mangled
159
+
160
+ def _binding_scope(self, name: str, at: ast.AST) -> ast.AST | None:
161
+ scope = self._scope(at)
162
+ skip_classes = False
163
+ while scope is not None:
164
+ if isinstance(scope, ast.ClassDef) and skip_classes:
165
+ scope = self._scope(scope)
166
+ continue
167
+ declarations = self.inventory.bindings.get(scope, {}).get(name, ())
168
+ globals_here = any(isinstance(item, ast.Global) for item in declarations)
169
+ if globals_here and scope is not self.context.tree:
170
+ if _declares_value(declarations):
171
+ return scope
172
+ scope = self.context.tree
173
+ continue
174
+ if _declares_value(declarations) or name in _type_parameters(scope):
175
+ return scope
176
+ skip_classes |= isinstance(scope, _FUNCTION_SCOPES)
177
+ scope = self._scope(scope)
178
+ return None
179
+
180
+ def _scope(self, node: ast.AST) -> ast.AST | None:
181
+ original = node
182
+ parent = self.context.parents.get(node)
183
+ while parent is not None:
184
+ if isinstance(parent, ast.TypeAlias):
185
+ return parent
186
+ if isinstance(parent, ast.comprehension) and parent.iter is node:
187
+ owner = self.context.parents.get(parent)
188
+ if (
189
+ isinstance(owner, ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp)
190
+ and owner.generators[0] is parent
191
+ ):
192
+ return self._scope(owner)
193
+ if isinstance(parent, (ast.Module, ast.ClassDef, *_FUNCTION_SCOPES)):
194
+ break
195
+ node, parent = parent, self.context.parents.get(parent)
196
+ return self.inventory.scope(original)
197
+
198
+ def _qualified(self, expression: ast.expr, seen: frozenset[ast.AST], *, assignments: bool) -> str | None:
199
+ if expression in seen:
200
+ return None
201
+ seen |= {expression}
202
+ if isinstance(expression, ast.Attribute):
203
+ base = self._qualified(expression.value, seen, assignments=assignments)
204
+ return None if base is None else f"{base}.{expression.attr}"
205
+ if not isinstance(expression, ast.Name):
206
+ return None
207
+ scope = self._binding_scope(expression.id, expression)
208
+ if scope is None:
209
+ return f"builtins.{expression.id}" if expression.id in _NATIVE_NAMES else None
210
+ declarations = self.inventory.bindings.get(scope, {}).get(expression.id, ())
211
+ declarations = tuple(item for item in declarations if not self._inplace_target(item))
212
+ if not declarations:
213
+ return None
214
+ origins = {self._declaration_origin(item, seen, assignments=assignments) for item in declarations}
215
+ if len(origins) != 1:
216
+ return None
217
+ return next(iter(origins))
218
+
219
+ def _declaration_origin(self, node: ast.AST, seen: frozenset[ast.AST], *, assignments: bool) -> str | None:
220
+ if node in seen:
221
+ return None
222
+ parent = self.context.parents.get(node)
223
+ if isinstance(node, ast.alias):
224
+ if isinstance(parent, ast.Import):
225
+ return node.name if node.asname else node.name.partition(".")[0]
226
+ if isinstance(parent, ast.ImportFrom) and parent.level == 0 and parent.module is not None:
227
+ return f"{parent.module}.{node.name}"
228
+ if assignments and isinstance(node, ast.Name) and (value := self._assignment(node)) is not None:
229
+ return self._qualified(value, seen | {node}, assignments=assignments)
230
+ return None
231
+
232
+ def _assignment(self, node: ast.Name) -> ast.expr | None:
233
+ parent = self.context.parents.get(node)
234
+ if isinstance(parent, ast.Assign) and node in parent.targets:
235
+ return parent.value
236
+ if isinstance(parent, ast.AnnAssign) and parent.target is node:
237
+ return parent.value
238
+ if isinstance(parent, ast.NamedExpr) and parent.target is node:
239
+ return parent.value
240
+ return None
241
+
242
+ def _inplace_target(self, node: ast.AST) -> bool:
243
+ parent = self.context.parents.get(node)
244
+ return isinstance(parent, ast.AugAssign) and parent.target is node
245
+
246
+
247
+ def _declares_value(declarations: Iterable[ast.AST]) -> bool:
248
+ return any(not isinstance(item, ast.Global) for item in declarations)
249
+
250
+
251
+ def _type_parameters(scope: ast.AST) -> frozenset[str]:
252
+ if isinstance(scope, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef | ast.TypeAlias):
253
+ return frozenset(
254
+ parameter.name
255
+ for parameter in scope.type_params
256
+ if isinstance(parameter, ast.TypeVar | ast.ParamSpec | ast.TypeVarTuple)
257
+ )
258
+ return frozenset()
@@ -76,7 +76,9 @@ def _constructor_hooks(rule: type[Rule]) -> _ConstructorHooks | None:
76
76
  or not _is_constructor_hook(metaclass_call)
77
77
  ):
78
78
  return None
79
- code: object = getattr(initializer, "__code__", None)
79
+ code: object = getattr( # sarj-noqa: SARJ557 -- Validate dynamically imported plugin classes and optional native constructor metadata.
80
+ initializer, "__code__", None
81
+ )
80
82
  if code is not None and not isinstance(code, CodeType):
81
83
  return None
82
84
  return _ConstructorHooks(initializer, code, allocator, metaclass_call)
@@ -143,7 +145,9 @@ class _RegistryState:
143
145
  if entry.module in sys.modules:
144
146
  self.external_modules.add(entry.module)
145
147
  module = import_module(entry.module)
146
- value: object = getattr(module, entry.class_name, None)
148
+ value: object = getattr( # sarj-noqa: SARJ557 -- Validate dynamically imported plugin classes and optional native constructor metadata.
149
+ module, entry.class_name, None
150
+ )
147
151
  if not isinstance(value, type) or not issubclass(value, Rule):
148
152
  msg = f"{entry.rule_id}: descriptor does not resolve to a Rule class"
149
153
  raise TypeError(msg)
@@ -201,7 +205,10 @@ class _RegistryState:
201
205
  module is None
202
206
  or rule is None
203
207
  or observed is None
204
- or getattr(module, entry.class_name, None) is not rule
208
+ or getattr( # sarj-noqa: SARJ557 -- Validate dynamically imported plugin classes and optional native constructor metadata.
209
+ module, entry.class_name, None
210
+ )
211
+ is not rule
205
212
  ):
206
213
  return None
207
214
  if (
@@ -11,6 +11,7 @@ from sqlglot.errors import SqlglotError
11
11
 
12
12
  from sarj_python_lint.rules._annotation_semantics import AnnotationSemantics, bit_or_members, scope_bound_names
13
13
  from sarj_python_lint.rules._ast_index import walk
14
+ from sarj_python_lint.rules._ast_position import ast_position
14
15
  from sarj_python_lint.rules._paths import is_generated
15
16
  from sarj_python_lint.rules._project_index import ProjectIndexSet
16
17
 
@@ -19,6 +20,7 @@ if TYPE_CHECKING:
19
20
  from collections.abc import Iterable, Iterator
20
21
 
21
22
  from sarj_python_lint._file_context import PythonFileContext
23
+ from sarj_python_lint.rules._ast_position import AstPosition
22
24
  from sarj_python_lint.rules._imports import ImportIndex
23
25
  from sarj_python_lint.rules._resource_provenance import ResourceProvenance
24
26
 
@@ -433,12 +435,6 @@ class _ReceiverOrigin:
433
435
  attribute: str | None = None
434
436
 
435
437
 
436
- @dataclass(frozen=True, order=True, slots=True)
437
- class _SourcePosition:
438
- line: int
439
- column: int
440
-
441
-
442
438
  class _ReceiverAliases:
443
439
  def __init__(self, context: PythonFileContext) -> None:
444
440
  self.context = context
@@ -535,8 +531,8 @@ class _ReceiverAliases:
535
531
  return frozenset()
536
532
 
537
533
 
538
- def _source_position(node: ast.AST) -> _SourcePosition:
539
- return _SourcePosition(getattr(node, "lineno", -1), getattr(node, "col_offset", -1))
534
+ def _source_position(node: ast.AST) -> AstPosition:
535
+ return ast_position(node, missing=-1)
540
536
 
541
537
 
542
538
  class StoreOwners:
@@ -196,7 +196,7 @@ def _returns_local_router(function: ast.FunctionDef | ast.AsyncFunctionDef, call
196
196
  for node in function.body
197
197
  if isinstance(node, (ast.Assign, ast.AnnAssign))
198
198
  for target in _assignment_targets(node)
199
- if isinstance(target, ast.Name) and getattr(node, "value", None) is call
199
+ if isinstance(target, ast.Name) and node.value is call
200
200
  }
201
201
  return bool(names) and any(
202
202
  isinstance(node, ast.Return) and isinstance(node.value, ast.Name) and node.value.id in names
@@ -251,7 +251,7 @@ def _resolve_annotation(node: ast.expr | None) -> ast.expr | None:
251
251
  if isinstance(node, ast.Constant) and isinstance(node.value, str):
252
252
  try:
253
253
  return ast.parse(node.value.strip(), mode="eval").body
254
- except SyntaxError:
254
+ except SyntaxError, UnicodeError:
255
255
  return None
256
256
  return node
257
257