sarj-python-lint 1.64.0__tar.gz → 1.66.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/.gitignore +3 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/PKG-INFO +1 -1
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/pyproject.toml +1 -1
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_file_context.py +19 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/cast_access.py +3 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_annotation_semantics.py +4 -1
- sarj_python_lint-1.66.1/src/sarj_python_lint/rules/_ast_names.py +63 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_closed_helpers.py +10 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_comments.py +30 -4
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_copy_on_write.py +2 -4
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_data_union_discriminator.py +1 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_docstrings.py +19 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_pydantic_field_contract.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_resource_provenance.py +39 -28
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_sql.py +25 -15
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_store_writes.py +3 -3
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +2 -23
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +2 -21
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +3 -3
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/named_record_at_boundaries.py +1 -1
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_any_mapping_types.py +37 -36
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +3 -3
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +2 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_discarded_validation_result.py +3 -7
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +1 -1
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_generic_single_export_module.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -9
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_loguru_printf_format.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_mutating_container_transform.py +1 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_partial_type_is.py +2 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +32 -33
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +1 -7
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_select_star.py +74 -38
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +4 -10
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +1 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/over_mocked_test.py +1 -12
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_class_row.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +2 -12
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +6 -24
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +1 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +20 -11
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +2 -4
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +2 -4
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +2 -4
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +1 -1
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_native_string_check.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +3 -3
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +1 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +2 -11
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +1 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +1 -15
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +3 -3
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +1 -9
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +2 -21
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_precise_factory_signature.py +2 -18
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +1 -13
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +4 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +1 -6
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/restated_test_docstring.py +11 -25
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +1 -10
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/typed_error_reasons.py +1 -8
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +1 -15
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/unused_mock_setup.py +3 -14
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/unused_test_factory_option.py +2 -2
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/LICENSE +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/README.md +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/suppression_policy.py +0 -0
|
@@ -44,6 +44,9 @@ Thumbs.db
|
|
|
44
44
|
!.claude/skills/test-value/
|
|
45
45
|
.claude/skills/test-value/*
|
|
46
46
|
!.claude/skills/test-value/SKILL.md
|
|
47
|
+
!.claude/skills/100-passes/
|
|
48
|
+
.claude/skills/100-passes/*
|
|
49
|
+
!.claude/skills/100-passes/SKILL.md
|
|
47
50
|
apps/docs/.wrangler/
|
|
48
51
|
apps/docs/.dev.vars*
|
|
49
52
|
!apps/docs/.dev.vars.example
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.66.1
|
|
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/
|
|
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, final
|
|
|
6
6
|
|
|
7
7
|
from sarj_python_lint._analysis_session import AnalysisSession
|
|
8
8
|
from sarj_python_lint._source import symbol_prefilter_source
|
|
9
|
+
from sarj_python_lint.rules._annotation_semantics import AnnotationSemantics
|
|
9
10
|
from sarj_python_lint.rules._ast_index import NodeIndex
|
|
10
11
|
from sarj_python_lint.rules._fastapi import FastapiIndex
|
|
11
12
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
@@ -18,6 +19,7 @@ if TYPE_CHECKING:
|
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
|
|
20
21
|
from sarj_python_lint.rules._copy_on_write import CopyOnWriteAnalysis
|
|
22
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
21
23
|
from sarj_python_lint.rules._test_provenance import TestProvenance
|
|
22
24
|
|
|
23
25
|
|
|
@@ -91,6 +93,10 @@ class PythonFileContext:
|
|
|
91
93
|
def module_imports(self) -> ImportIndex:
|
|
92
94
|
return ImportIndex.from_tree(self._valid_tree(), module_scope_only=True)
|
|
93
95
|
|
|
96
|
+
@cached_property
|
|
97
|
+
def annotation_semantics(self) -> AnnotationSemantics:
|
|
98
|
+
return AnnotationSemantics.from_imports(self._valid_tree(), self.module_imports)
|
|
99
|
+
|
|
94
100
|
@cached_property
|
|
95
101
|
def dynamic_namespace(self) -> bool:
|
|
96
102
|
return dynamic_namespace_access(self.nodes(ast.AST), self.imports, self.parents)
|
|
@@ -117,3 +123,16 @@ class PythonFileContext:
|
|
|
117
123
|
from sarj_python_lint.rules._copy_on_write import CopyOnWriteAnalysis # ruff: ignore[import-outside-top-level] — share ownership analysis only when requested
|
|
118
124
|
|
|
119
125
|
return CopyOnWriteAnalysis(self)
|
|
126
|
+
|
|
127
|
+
@cached_property
|
|
128
|
+
def resource_provenance(self) -> ResourceProvenance:
|
|
129
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance # ruff: ignore[import-outside-top-level] -- file-owned binding facts are lazy.
|
|
130
|
+
|
|
131
|
+
# The owner caches this analysis; borrowing prevents a reference cycle.
|
|
132
|
+
return ResourceProvenance(self, borrow_context=True)
|
|
133
|
+
|
|
134
|
+
@cached_property
|
|
135
|
+
def static_resource_provenance(self) -> ResourceProvenance:
|
|
136
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance # ruff: ignore[import-outside-top-level] -- keep static-namespace policy in a separate cached analysis.
|
|
137
|
+
|
|
138
|
+
return ResourceProvenance(self, require_static_namespace=True, borrow_context=True)
|
|
@@ -6,13 +6,14 @@ from typing import TYPE_CHECKING, Literal
|
|
|
6
6
|
|
|
7
7
|
from sarj_python_lint._file_context import PythonFileContext
|
|
8
8
|
from sarj_python_lint.rules._namespace_access import dynamic_namespace_access
|
|
9
|
-
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
if TYPE_CHECKING:
|
|
13
12
|
from collections.abc import Iterator
|
|
14
13
|
from pathlib import Path
|
|
15
14
|
|
|
15
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
16
|
+
|
|
16
17
|
|
|
17
18
|
_TYPING = frozenset({"typing", "typing_extensions"})
|
|
18
19
|
_GETATTR_ARITIES = frozenset({2, 3})
|
|
@@ -44,7 +45,7 @@ def indirect_typing_cast_accesses(path: Path, source: str) -> tuple[CastAccess,
|
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def _reflection_accesses(context: PythonFileContext) -> list[CastAccess]:
|
|
47
|
-
provenance =
|
|
48
|
+
provenance = context.resource_provenance
|
|
48
49
|
candidates = [
|
|
49
50
|
(node, receiver)
|
|
50
51
|
for node in context.nodes(ast.Call, ast.Subscript)
|
|
@@ -19,7 +19,10 @@ class AnnotationSemantics:
|
|
|
19
19
|
|
|
20
20
|
@classmethod
|
|
21
21
|
def from_tree(cls, tree: ast.Module, *, module_scope_only: bool = True) -> Self:
|
|
22
|
-
|
|
22
|
+
return cls.from_imports(tree, ImportIndex.from_tree(tree, module_scope_only=module_scope_only))
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_imports(cls, tree: ast.Module, imports: ImportIndex) -> Self:
|
|
23
26
|
candidates: dict[str, ast.expr] = {}
|
|
24
27
|
ambiguous: set[str] = set()
|
|
25
28
|
for statement in tree.body:
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def root_name(node: ast.expr) -> str | None:
|
|
7
|
+
while isinstance(node, ast.Attribute):
|
|
8
|
+
node = node.value
|
|
9
|
+
return node.id if isinstance(node, ast.Name) else None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def expression_root(node: ast.expr) -> str | None:
|
|
13
|
+
while isinstance(node, (ast.Attribute, ast.Subscript)):
|
|
14
|
+
node = node.value
|
|
15
|
+
return node.id if isinstance(node, ast.Name) else None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def terminal_name(node: ast.expr) -> str | None:
|
|
19
|
+
match node:
|
|
20
|
+
case ast.Name(id=name) | ast.Attribute(attr=name):
|
|
21
|
+
return name
|
|
22
|
+
case _:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def unqualified_name(node: ast.expr) -> str:
|
|
27
|
+
match node:
|
|
28
|
+
case ast.Name(id=name) | ast.Attribute(attr=name):
|
|
29
|
+
return name
|
|
30
|
+
case _:
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def dotted_parts(node: ast.expr) -> tuple[str, ...] | None:
|
|
35
|
+
parts: list[str] = []
|
|
36
|
+
current = node
|
|
37
|
+
while isinstance(current, ast.Attribute):
|
|
38
|
+
parts.append(current.attr)
|
|
39
|
+
current = current.value
|
|
40
|
+
if not isinstance(current, ast.Name):
|
|
41
|
+
return None
|
|
42
|
+
parts.append(current.id)
|
|
43
|
+
return tuple(reversed(parts))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def dotted_name(node: ast.expr) -> str | None:
|
|
47
|
+
parts = dotted_parts(node)
|
|
48
|
+
return ".".join(parts) if parts is not None else None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def assignment_names(node: ast.AST) -> set[str]:
|
|
52
|
+
match node:
|
|
53
|
+
case ast.Name(id=name):
|
|
54
|
+
return {name}
|
|
55
|
+
case ast.Tuple() | ast.List():
|
|
56
|
+
names: set[str] = set()
|
|
57
|
+
for element in node.elts:
|
|
58
|
+
names.update(assignment_names(element))
|
|
59
|
+
return names
|
|
60
|
+
case ast.Starred(value=value):
|
|
61
|
+
return assignment_names(value)
|
|
62
|
+
case _:
|
|
63
|
+
return set()
|
{sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_closed_helpers.py
RENAMED
|
@@ -183,3 +183,13 @@ def _plain_decorator(
|
|
|
183
183
|
and not (keyword.arg == "mode" and isinstance(keyword.value, ast.Constant) and keyword.value.value == "wrap")
|
|
184
184
|
for keyword in decorator.keywords
|
|
185
185
|
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def closed_owner(function: ast.FunctionDef | ast.AsyncFunctionDef, context: PythonFileContext) -> bool:
|
|
189
|
+
if not isinstance(function, ast.FunctionDef) or not function.name.startswith("_"):
|
|
190
|
+
return False
|
|
191
|
+
references = closed_references(context, function, context.resource_provenance)
|
|
192
|
+
return bool(references) and all(
|
|
193
|
+
(call := direct_call(reference, context)) is not None and call_matches(function, call)
|
|
194
|
+
for reference in references
|
|
195
|
+
)
|
|
@@ -326,7 +326,13 @@ class _Scan(NamedTuple):
|
|
|
326
326
|
ordered: _Ordered
|
|
327
327
|
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
class _ScanMemo(NamedTuple):
|
|
330
|
+
source: str
|
|
331
|
+
scan: _Scan | None
|
|
332
|
+
comment_lines: frozenset[int] | None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
_last_scan: _ScanMemo | None = None
|
|
330
336
|
|
|
331
337
|
|
|
332
338
|
def all_comments(source: str) -> _CommentScan[_Ordered]:
|
|
@@ -334,15 +340,35 @@ def all_comments(source: str) -> _CommentScan[_Ordered]:
|
|
|
334
340
|
return _CommentScan(scan.ordered, scan.first_code_line)
|
|
335
341
|
|
|
336
342
|
|
|
343
|
+
def comment_line_numbers(source: str) -> frozenset[int]:
|
|
344
|
+
global _last_scan # ruff: ignore[global-statement] -- retain the existing single-source memo for both comment projections.
|
|
345
|
+
cached = _last_scan
|
|
346
|
+
scan = cached.scan if cached is not None and cached.source is source else None
|
|
347
|
+
if cached is not None and cached.source is source and cached.comment_lines is not None:
|
|
348
|
+
return cached.comment_lines
|
|
349
|
+
lines: frozenset[int]
|
|
350
|
+
if scan is not None:
|
|
351
|
+
lines = frozenset(comment.line for comment in scan.ordered)
|
|
352
|
+
else:
|
|
353
|
+
try:
|
|
354
|
+
tokens = tokenize.generate_tokens(io.StringIO(source).readline)
|
|
355
|
+
lines = frozenset(token.start[0] for token in tokens if token.type == tokenize.COMMENT)
|
|
356
|
+
except IndentationError, tokenize.TokenError:
|
|
357
|
+
lines = frozenset()
|
|
358
|
+
_last_scan = _ScanMemo(source, scan, lines)
|
|
359
|
+
return lines
|
|
360
|
+
|
|
361
|
+
|
|
337
362
|
def _scan_memo(source: str) -> _Scan:
|
|
338
363
|
global _last_scan # ruff: ignore[global-statement] — single-slot memo; the CLI runs rules per file sequentially
|
|
339
|
-
if _last_scan is not None and _last_scan
|
|
340
|
-
|
|
364
|
+
cached = _last_scan if _last_scan is not None and _last_scan.source is source else None
|
|
365
|
+
if cached is not None and cached.scan is not None:
|
|
366
|
+
return cached.scan
|
|
341
367
|
try:
|
|
342
368
|
result = _scan(source)
|
|
343
369
|
except UnicodeError:
|
|
344
370
|
result = _Scan([], [], set(), 1 << 30, [])
|
|
345
|
-
_last_scan = (source, result)
|
|
371
|
+
_last_scan = _ScanMemo(source, result, cached.comment_lines if cached is not None else None)
|
|
346
372
|
return result
|
|
347
373
|
|
|
348
374
|
|
{sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_copy_on_write.py
RENAMED
|
@@ -110,9 +110,7 @@ class _Models:
|
|
|
110
110
|
def validation_owner(self, unit: SourceUnit) -> _ValidationOwner:
|
|
111
111
|
if unit.path not in self._validation_owners:
|
|
112
112
|
context = PythonFileContext(unit.path, unit.source, self._validation_session)
|
|
113
|
-
self._validation_owners[unit.path] = _ValidationOwner(
|
|
114
|
-
context, ResourceProvenance(context, require_static_namespace=True)
|
|
115
|
-
)
|
|
113
|
+
self._validation_owners[unit.path] = _ValidationOwner(context, context.static_resource_provenance)
|
|
116
114
|
return self._validation_owners[unit.path]
|
|
117
115
|
|
|
118
116
|
def annotations(self, unit: SourceUnit) -> AnnotationSemantics:
|
|
@@ -378,7 +376,7 @@ class CopyOnWriteAnalysis:
|
|
|
378
376
|
|
|
379
377
|
@cached_property
|
|
380
378
|
def _validator_provider(self) -> ResourceProvenance:
|
|
381
|
-
return
|
|
379
|
+
return self.context.static_resource_provenance
|
|
382
380
|
|
|
383
381
|
def _function(self, function: _Function) -> None:
|
|
384
382
|
parameters = [*function.args.posonlyargs, *function.args.args, *function.args.kwonlyargs]
|
|
@@ -72,6 +72,7 @@ def data_union_diagnostics(context: PythonFileContext, code: str) -> list[Diagno
|
|
|
72
72
|
class _DataUnions:
|
|
73
73
|
def __init__(self, context: PythonFileContext, tree: ast.Module) -> None:
|
|
74
74
|
self.context = context
|
|
75
|
+
# This analysis refines mutation ownership; keep its provenance private.
|
|
75
76
|
self.provenance = ResourceProvenance(context, require_static_namespace=True)
|
|
76
77
|
self.provenance.mutated = _scoped_module_mutations(self.provenance, tree)
|
|
77
78
|
bindings = self.provenance.bindings.get(tree, {})
|
{sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
@@ -13,9 +13,16 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from collections.abc import Iterable
|
|
14
14
|
|
|
15
15
|
from sarj_python_lint.rules._ast_index import NodeIndex
|
|
16
|
+
from sarj_python_lint.rules._imports import ImportIndex
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
_GETATTR_MIN_ARGS = 2
|
|
20
|
+
_RUNTIME_DOC_DECORATORS = (
|
|
21
|
+
(frozenset({"agents"}), "function_tool"),
|
|
22
|
+
(frozenset({"click"}), "command"),
|
|
23
|
+
(frozenset({"typer"}), "command"),
|
|
24
|
+
)
|
|
25
|
+
_OVERLOAD_MODULES = frozenset({"typing", "typing_extensions"})
|
|
19
26
|
|
|
20
27
|
# Docstring filler that says nothing about *which* thing is being described.
|
|
21
28
|
_BASE_STOPWORDS = frozenset(
|
|
@@ -259,6 +266,18 @@ def decorator_markers(node: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDe
|
|
|
259
266
|
return markers
|
|
260
267
|
|
|
261
268
|
|
|
269
|
+
def is_runtime_consumed_or_overload(node: ast.FunctionDef | ast.AsyncFunctionDef, imports: ImportIndex) -> bool:
|
|
270
|
+
if decorator_markers(node) & PROMPT_DECORATOR_MARKERS:
|
|
271
|
+
return True
|
|
272
|
+
for decorator in node.decorator_list:
|
|
273
|
+
target = decorator.func if isinstance(decorator, ast.Call) else decorator
|
|
274
|
+
if imports.resolves(target, sources=_OVERLOAD_MODULES, symbol="overload"):
|
|
275
|
+
return True
|
|
276
|
+
if any(imports.resolves(target, sources=sources, symbol=symbol) for sources, symbol in _RUNTIME_DOC_DECORATORS):
|
|
277
|
+
return True
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
|
|
262
281
|
def docstring_expression(
|
|
263
282
|
node: ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
|
|
264
283
|
) -> ast.Expr | None:
|
|
@@ -13,11 +13,11 @@ from sarj_python_lint.json_boundary import is_object_mapping
|
|
|
13
13
|
from sarj_python_lint.rules._first_party import distribution_root
|
|
14
14
|
from sarj_python_lint.rules._namespace_access import dynamic_namespace_access
|
|
15
15
|
from sarj_python_lint.rules._native_namespace_capability import native_namespace_capability_escapes
|
|
16
|
-
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
if TYPE_CHECKING:
|
|
20
19
|
from sarj_python_lint._file_context import PythonFileContext
|
|
20
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
MODEL_SOURCES = frozenset({"pydantic", "pydantic.main"})
|
|
@@ -43,7 +43,7 @@ def direct_models(context: PythonFileContext, *, methods: bool = False) -> tuple
|
|
|
43
43
|
or dynamic_namespace_access(context.nodes(ast.AST), context.imports, context.parents)
|
|
44
44
|
):
|
|
45
45
|
return ()
|
|
46
|
-
provenance =
|
|
46
|
+
provenance = context.static_resource_provenance
|
|
47
47
|
return tuple(
|
|
48
48
|
node
|
|
49
49
|
for node in tree.body
|
|
@@ -5,9 +5,9 @@ from collections import defaultdict
|
|
|
5
5
|
from functools import cached_property
|
|
6
6
|
from types import MappingProxyType
|
|
7
7
|
from typing import TYPE_CHECKING, final
|
|
8
|
+
from weakref import ReferenceType, ref
|
|
8
9
|
|
|
9
10
|
from sarj_python_lint.rules._ast_index import has_own_yield
|
|
10
|
-
from sarj_python_lint.rules._namespace_access import dynamic_namespace_access
|
|
11
11
|
from sarj_python_lint.rules._runtime_bindings import RuntimeBindings
|
|
12
12
|
|
|
13
13
|
|
|
@@ -71,8 +71,13 @@ def builtin_namespace_untouched(context: PythonFileContext) -> bool:
|
|
|
71
71
|
|
|
72
72
|
@final
|
|
73
73
|
class ResourceProvenance:
|
|
74
|
-
def __init__(
|
|
75
|
-
self
|
|
74
|
+
def __init__(
|
|
75
|
+
self, context: PythonFileContext, *, require_static_namespace: bool = False, borrow_context: bool = False
|
|
76
|
+
) -> None:
|
|
77
|
+
self._context: PythonFileContext | ReferenceType[PythonFileContext] = (
|
|
78
|
+
ref(context) if borrow_context else context
|
|
79
|
+
)
|
|
80
|
+
self.parents = context.parents
|
|
76
81
|
self.require_static_namespace = require_static_namespace
|
|
77
82
|
self.bindings: dict[ast.AST | None, dict[str, list[ast.AST]]] = defaultdict(lambda: defaultdict(list))
|
|
78
83
|
self.reads: dict[str, list[ast.Name]] = defaultdict(list)
|
|
@@ -98,15 +103,23 @@ class ResourceProvenance:
|
|
|
98
103
|
if root is not None:
|
|
99
104
|
self.mutated.add(root)
|
|
100
105
|
|
|
106
|
+
@property
|
|
107
|
+
def context(self) -> PythonFileContext:
|
|
108
|
+
context = self._context() if isinstance(self._context, ReferenceType) else self._context
|
|
109
|
+
if context is None:
|
|
110
|
+
msg = "The provenance file context has been released"
|
|
111
|
+
raise ReferenceError(msg)
|
|
112
|
+
return context
|
|
113
|
+
|
|
101
114
|
@cached_property
|
|
102
115
|
def builtin_namespace_untouched(self) -> bool:
|
|
103
116
|
return builtin_namespace_untouched(self.context)
|
|
104
117
|
|
|
105
118
|
def scope(self, node: ast.AST) -> ast.AST | None:
|
|
106
|
-
parent = self.
|
|
119
|
+
parent = self.parents.get(node)
|
|
107
120
|
while parent is not None:
|
|
108
121
|
if isinstance(parent, ast.arguments) and (node in parent.defaults or node in parent.kw_defaults):
|
|
109
|
-
parent = self.
|
|
122
|
+
parent = self.parents.get(parent)
|
|
110
123
|
elif isinstance(parent, _SCOPES) and not (
|
|
111
124
|
(
|
|
112
125
|
isinstance(parent, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef)
|
|
@@ -118,12 +131,12 @@ class ResourceProvenance:
|
|
|
118
131
|
if parent is None:
|
|
119
132
|
return None
|
|
120
133
|
node = parent
|
|
121
|
-
parent = self.
|
|
134
|
+
parent = self.parents.get(parent)
|
|
122
135
|
return None
|
|
123
136
|
|
|
124
137
|
def _binding_scope(self, node: ast.AST) -> ast.AST | None:
|
|
125
138
|
scope = self.scope(node)
|
|
126
|
-
parent = self.
|
|
139
|
+
parent = self.parents.get(node)
|
|
127
140
|
if isinstance(parent, ast.NamedExpr) and parent.target is node:
|
|
128
141
|
while isinstance(scope, ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp):
|
|
129
142
|
scope = self.scope(scope)
|
|
@@ -155,7 +168,7 @@ class ResourceProvenance:
|
|
|
155
168
|
binding = self.runtime_binding(root, node)
|
|
156
169
|
if not isinstance(binding, ast.alias):
|
|
157
170
|
return None
|
|
158
|
-
qualified = import_name_for_binding(node, binding, self.
|
|
171
|
+
qualified = import_name_for_binding(node, binding, self.parents.get(binding))
|
|
159
172
|
if qualified is None or not self._owner_namespace_untouched(qualified.partition(".")[0]):
|
|
160
173
|
return None
|
|
161
174
|
if binding not in self.runtime_import_cache:
|
|
@@ -195,7 +208,7 @@ class ResourceProvenance:
|
|
|
195
208
|
def _namespace_aliases(self) -> dict[str, tuple[ast.alias, ...]]:
|
|
196
209
|
aliases: dict[str, list[ast.alias]] = defaultdict(list)
|
|
197
210
|
for binding in self.context.nodes(ast.alias):
|
|
198
|
-
parent = self.
|
|
211
|
+
parent = self.parents.get(binding)
|
|
199
212
|
name = binding.asname or (
|
|
200
213
|
binding.name.partition(".")[0] if isinstance(parent, ast.Import) else binding.name
|
|
201
214
|
)
|
|
@@ -231,7 +244,7 @@ class ResourceProvenance:
|
|
|
231
244
|
return paths
|
|
232
245
|
|
|
233
246
|
def _namespace_module_path(self, binding: ast.alias, attributes: tuple[str, ...]) -> Path | None:
|
|
234
|
-
parent = self.
|
|
247
|
+
parent = self.parents.get(binding)
|
|
235
248
|
match parent:
|
|
236
249
|
case ast.Import():
|
|
237
250
|
prefix = binding.name if binding.asname else binding.name.partition(".")[0]
|
|
@@ -250,7 +263,7 @@ class ResourceProvenance:
|
|
|
250
263
|
for node in nodes:
|
|
251
264
|
if not isinstance(node, ast.alias):
|
|
252
265
|
return False
|
|
253
|
-
parent = self.
|
|
266
|
+
parent = self.parents.get(node)
|
|
254
267
|
if isinstance(parent, ast.Import):
|
|
255
268
|
targets.add(("import", None, 0, node.name))
|
|
256
269
|
elif isinstance(parent, ast.ImportFrom):
|
|
@@ -271,7 +284,7 @@ class ResourceProvenance:
|
|
|
271
284
|
return False
|
|
272
285
|
binding = self.binding(root, node)
|
|
273
286
|
for read in self.reads.get(root, ()):
|
|
274
|
-
parent = self.
|
|
287
|
+
parent = self.parents.get(read)
|
|
275
288
|
if isinstance(parent, ast.Attribute | ast.ExceptHandler):
|
|
276
289
|
continue
|
|
277
290
|
if not self._safe_import_use(read):
|
|
@@ -317,7 +330,7 @@ class ResourceProvenance:
|
|
|
317
330
|
)
|
|
318
331
|
|
|
319
332
|
def _safe_import_use(self, node: ast.Name | ast.Attribute) -> bool:
|
|
320
|
-
parent = self.
|
|
333
|
+
parent = self.parents.get(node)
|
|
321
334
|
return (
|
|
322
335
|
(isinstance(parent, ast.Attribute) and parent.value is node)
|
|
323
336
|
or (isinstance(parent, ast.Call) and parent.func is node)
|
|
@@ -337,9 +350,9 @@ class ResourceProvenance:
|
|
|
337
350
|
binding = self.binding(name.id, at)
|
|
338
351
|
if not isinstance(binding, ast.Name):
|
|
339
352
|
return None
|
|
340
|
-
statement = self.
|
|
353
|
+
statement = self.parents.get(binding)
|
|
341
354
|
if isinstance(statement, ast.withitem):
|
|
342
|
-
owner = self.
|
|
355
|
+
owner = self.parents.get(statement)
|
|
343
356
|
if (
|
|
344
357
|
binding is statement.optional_vars
|
|
345
358
|
and self.contains(owner, at)
|
|
@@ -351,12 +364,12 @@ class ResourceProvenance:
|
|
|
351
364
|
return value if isinstance(value, ast.Call) else None
|
|
352
365
|
|
|
353
366
|
def assignment_value(self, binding: ast.Name, at: ast.AST) -> ast.expr | None:
|
|
354
|
-
statement = self.
|
|
367
|
+
statement = self.parents.get(binding)
|
|
355
368
|
if not isinstance(statement, ast.Assign | ast.AnnAssign):
|
|
356
369
|
return None
|
|
357
370
|
if isinstance(statement, ast.Assign) and len(statement.targets) != 1:
|
|
358
371
|
return None
|
|
359
|
-
if not isinstance(self.
|
|
372
|
+
if not isinstance(self.parents.get(statement), ast.Module | ast.FunctionDef | ast.AsyncFunctionDef):
|
|
360
373
|
return None
|
|
361
374
|
return statement.value if statement.lineno < getattr(at, "lineno", 0) else None
|
|
362
375
|
|
|
@@ -381,12 +394,10 @@ class ResourceProvenance:
|
|
|
381
394
|
|
|
382
395
|
@cached_property
|
|
383
396
|
def dynamic_namespace(self) -> bool:
|
|
384
|
-
return self.require_static_namespace and
|
|
385
|
-
self.context.nodes(ast.AST), self.context.imports, self.context.parents
|
|
386
|
-
)
|
|
397
|
+
return self.require_static_namespace and self.context.dynamic_namespace
|
|
387
398
|
|
|
388
399
|
def _stable_async_definition(self, function: ast.AsyncFunctionDef) -> ast.AsyncFunctionDef | None:
|
|
389
|
-
owner = self.
|
|
400
|
+
owner = self.parents.get(function)
|
|
390
401
|
if not isinstance(owner, ast.Module | ast.FunctionDef | ast.AsyncFunctionDef):
|
|
391
402
|
return None
|
|
392
403
|
if function.decorator_list or has_own_yield(function):
|
|
@@ -415,10 +426,10 @@ class ResourceProvenance:
|
|
|
415
426
|
return True
|
|
416
427
|
|
|
417
428
|
def _function_alias(self, read: ast.Name) -> ast.Name | None:
|
|
418
|
-
parent = self.
|
|
429
|
+
parent = self.parents.get(read)
|
|
419
430
|
if not isinstance(parent, ast.Assign | ast.AnnAssign) or parent.value is not read:
|
|
420
431
|
return None
|
|
421
|
-
if not isinstance(self.
|
|
432
|
+
if not isinstance(self.parents.get(parent), ast.Module | ast.FunctionDef | ast.AsyncFunctionDef):
|
|
422
433
|
return None
|
|
423
434
|
targets = parent.targets if isinstance(parent, ast.Assign) else [parent.target]
|
|
424
435
|
if len(targets) != 1 or not isinstance(targets[0], ast.Name):
|
|
@@ -427,7 +438,7 @@ class ResourceProvenance:
|
|
|
427
438
|
return target if self.binding(target.id, target) is target else None
|
|
428
439
|
|
|
429
440
|
def _safe_async_read(self, read: ast.Name) -> bool:
|
|
430
|
-
parent = self.
|
|
441
|
+
parent = self.parents.get(read)
|
|
431
442
|
if not isinstance(parent, ast.Call):
|
|
432
443
|
return False
|
|
433
444
|
if parent.func is read:
|
|
@@ -469,7 +480,7 @@ class ResourceProvenance:
|
|
|
469
480
|
return self.escape_cache[key]
|
|
470
481
|
|
|
471
482
|
def _method_or_context_read(self, read: ast.Name, *, allow_context: bool) -> bool:
|
|
472
|
-
parent = self.
|
|
483
|
+
parent = self.parents.get(read)
|
|
473
484
|
if allow_context and isinstance(parent, ast.withitem) and parent.context_expr is read:
|
|
474
485
|
return True
|
|
475
486
|
if (
|
|
@@ -478,12 +489,12 @@ class ResourceProvenance:
|
|
|
478
489
|
or parent.attr in {"__setattr__", "__delattr__", "__getattribute__"}
|
|
479
490
|
):
|
|
480
491
|
return False
|
|
481
|
-
call = self.
|
|
492
|
+
call = self.parents.get(parent)
|
|
482
493
|
return isinstance(call, ast.Call) and call.func is parent
|
|
483
494
|
|
|
484
495
|
def contains(self, owner: ast.AST | None, node: ast.AST) -> bool:
|
|
485
|
-
while node in self.
|
|
486
|
-
node = self.
|
|
496
|
+
while node in self.parents:
|
|
497
|
+
node = self.parents[node]
|
|
487
498
|
if node is owner:
|
|
488
499
|
return True
|
|
489
500
|
return False
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
+
import re
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
from sarj_python_lint.rules._paths import is_test_path
|
|
@@ -10,6 +11,15 @@ if TYPE_CHECKING:
|
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
_SQL_LINE_END = re.compile(r"[\r\n]")
|
|
15
|
+
_CONTINUED_QUOTE = re.compile(r"[ \t\f\v]*(?:--[^\r\n]*)?[\r\n](?:[ \t\f\v\r\n]|--[^\r\n]*[\r\n])*'")
|
|
16
|
+
POSTGRES_IDENTIFIER_CONTINUATION = r"[A-Za-z0-9_$\u0080-\U0010ffff]"
|
|
17
|
+
POSTGRES_UNQUOTED_IDENTIFIER = rf"[A-Za-z_\u0080-\U0010ffff]{POSTGRES_IDENTIFIER_CONTINUATION}*"
|
|
18
|
+
SQL_WHITESPACE = " \t\r\n\f\v"
|
|
19
|
+
_IDENTIFIER_CONTINUATION = re.compile(POSTGRES_IDENTIFIER_CONTINUATION)
|
|
20
|
+
_DOLLAR_DELIMITER = re.compile(r"\$(?:[A-Za-z_\u0080-\U0010ffff][A-Za-z0-9_\u0080-\U0010ffff]*)?\$")
|
|
21
|
+
|
|
22
|
+
|
|
13
23
|
def is_store_module(path: Path) -> bool:
|
|
14
24
|
if is_test_path(path):
|
|
15
25
|
return False
|
|
@@ -44,6 +54,7 @@ def strip_sql_noise(
|
|
|
44
54
|
*,
|
|
45
55
|
mask_dollar_quotes: bool = True,
|
|
46
56
|
mask_double_quotes: bool = True,
|
|
57
|
+
preserve_quoted_tokens: bool = False,
|
|
47
58
|
) -> str:
|
|
48
59
|
out = list(text)
|
|
49
60
|
i = 0
|
|
@@ -56,8 +67,10 @@ def strip_sql_noise(
|
|
|
56
67
|
i += 1
|
|
57
68
|
continue
|
|
58
69
|
for offset in range(i, end):
|
|
59
|
-
if text[offset]
|
|
70
|
+
if text[offset] not in {"\n", "\r"}:
|
|
60
71
|
out[offset] = " "
|
|
72
|
+
if preserve_quoted_tokens and text[i] in {"'", '"', "$"}:
|
|
73
|
+
out[i] = "?"
|
|
61
74
|
i = end
|
|
62
75
|
return "".join(out)
|
|
63
76
|
|
|
@@ -68,7 +81,7 @@ def _quoted_sql_end(text: str, start: int) -> int:
|
|
|
68
81
|
quote == "'"
|
|
69
82
|
and start > 0
|
|
70
83
|
and text[start - 1] in {"E", "e"}
|
|
71
|
-
and (start == 1 or
|
|
84
|
+
and (start == 1 or _IDENTIFIER_CONTINUATION.match(text, start - 2) is None)
|
|
72
85
|
)
|
|
73
86
|
i = start + 1
|
|
74
87
|
while i < len(text):
|
|
@@ -82,6 +95,9 @@ def _quoted_sql_end(text: str, start: int) -> int:
|
|
|
82
95
|
if i < len(text) and text[i] == quote:
|
|
83
96
|
i += 1
|
|
84
97
|
continue
|
|
98
|
+
if escape_string and (continuation := _CONTINUED_QUOTE.match(text, i)) is not None:
|
|
99
|
+
i = continuation.end()
|
|
100
|
+
continue
|
|
85
101
|
break
|
|
86
102
|
return i
|
|
87
103
|
|
|
@@ -91,8 +107,8 @@ def _sql_noise_end(text: str, start: int, *, mask_dollar_quotes: bool) -> int |
|
|
|
91
107
|
if ch in {"'", '"'}:
|
|
92
108
|
return _quoted_sql_end(text, start)
|
|
93
109
|
if text.startswith("--", start) or (ch == "#" and (start == 0 or text[start - 1].isspace())):
|
|
94
|
-
end =
|
|
95
|
-
return len(text) if end
|
|
110
|
+
end = _SQL_LINE_END.search(text, start)
|
|
111
|
+
return len(text) if end is None else end.start()
|
|
96
112
|
if text.startswith("/*", start):
|
|
97
113
|
return _block_comment_end(text, start)
|
|
98
114
|
if mask_dollar_quotes and ch == "$":
|
|
@@ -116,17 +132,11 @@ def _block_comment_end(text: str, start: int) -> int:
|
|
|
116
132
|
|
|
117
133
|
|
|
118
134
|
def _dollar_quote_end(text: str, start: int) -> int | None:
|
|
119
|
-
if start > 0 and (text
|
|
135
|
+
if start > 0 and _IDENTIFIER_CONTINUATION.match(text, start - 1) is not None:
|
|
120
136
|
return None
|
|
121
|
-
|
|
122
|
-
if
|
|
123
|
-
return None
|
|
124
|
-
tag = text[start + 1 : delimiter_end]
|
|
125
|
-
valid_tag = not tag or (
|
|
126
|
-
(tag[0].isalpha() or tag[0] == "_") and all(char.isalnum() or char == "_" for char in tag[1:])
|
|
127
|
-
)
|
|
128
|
-
if not valid_tag:
|
|
137
|
+
match = _DOLLAR_DELIMITER.match(text, start)
|
|
138
|
+
if match is None:
|
|
129
139
|
return None
|
|
130
|
-
delimiter =
|
|
131
|
-
close = text.find(delimiter,
|
|
140
|
+
delimiter = match[0]
|
|
141
|
+
close = text.find(delimiter, match.end())
|
|
132
142
|
return None if close == -1 else close + len(delimiter)
|
{sarj_python_lint-1.64.0 → sarj_python_lint-1.66.1}/src/sarj_python_lint/rules/_store_writes.py
RENAMED
|
@@ -12,7 +12,6 @@ from sarj_python_lint.rules._annotation_semantics import AnnotationSemantics, sc
|
|
|
12
12
|
from sarj_python_lint.rules._ast_index import walk
|
|
13
13
|
from sarj_python_lint.rules._paths import is_generated
|
|
14
14
|
from sarj_python_lint.rules._project_index import ProjectIndexSet
|
|
15
|
-
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
if TYPE_CHECKING:
|
|
@@ -20,6 +19,7 @@ if TYPE_CHECKING:
|
|
|
20
19
|
|
|
21
20
|
from sarj_python_lint._file_context import PythonFileContext
|
|
22
21
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
22
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
type Function = ast.FunctionDef | ast.AsyncFunctionDef
|
|
@@ -187,7 +187,7 @@ def _comprehension_scopes(
|
|
|
187
187
|
class StoreOwners:
|
|
188
188
|
def __init__(self, context: PythonFileContext) -> None:
|
|
189
189
|
self.context = context
|
|
190
|
-
self.provenance =
|
|
190
|
+
self.provenance = context.static_resource_provenance
|
|
191
191
|
self.project = context.session.project
|
|
192
192
|
|
|
193
193
|
def receiver(self, expression: ast.expr, at: ast.AST) -> tuple[ast.ClassDef, ImportIndex] | None:
|
|
@@ -290,7 +290,7 @@ class StoreOwners:
|
|
|
290
290
|
from sarj_python_lint._file_context import PythonFileContext # ruff: ignore[import-outside-top-level] -- defer to avoid the rule registry's context import cycle.
|
|
291
291
|
|
|
292
292
|
definition = PythonFileContext(owner.path, owner.source, self.context.session)
|
|
293
|
-
provenance =
|
|
293
|
+
provenance = definition.static_resource_provenance
|
|
294
294
|
if (
|
|
295
295
|
provenance.dynamic_namespace
|
|
296
296
|
or provenance.wildcard
|