sarj-python-lint 1.69.1__tar.gz → 1.70.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.69.1 → sarj_python_lint-1.70.1}/PKG-INFO +1 -1
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/pyproject.toml +1 -1
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_registry.py +2 -0
- sarj_python_lint-1.70.1/src/sarj_python_lint/rules/no_redundant_local_alias.py +397 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +4 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +4 -3
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/.gitignore +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/LICENSE +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/README.md +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_parallel.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/cast_access.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_names.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
- {sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/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.
|
|
3
|
+
Version: 1.70.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/
|
|
@@ -232,6 +232,7 @@ from sarj_python_lint.rules.no_impossible_pydantic_field_bounds import NoImpossi
|
|
|
232
232
|
from sarj_python_lint.rules.no_invalid_pydantic_bypass import NoInvalidPydanticBypass
|
|
233
233
|
from sarj_python_lint.rules.no_invalid_pydantic_validator_return import NoInvalidPydanticValidatorReturn
|
|
234
234
|
from sarj_python_lint.rules.no_partial_type_is import NoPartialTypeIs
|
|
235
|
+
from sarj_python_lint.rules.no_redundant_local_alias import NoRedundantLocalAlias
|
|
235
236
|
from sarj_python_lint.rules.no_shallow_record_type_guard import NoShallowRecordTypeGuard
|
|
236
237
|
from sarj_python_lint.rules.no_slash_collection_root_under_prefix import NoSlashCollectionRootUnderPrefix
|
|
237
238
|
from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
|
|
@@ -254,6 +255,7 @@ from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOr
|
|
|
254
255
|
|
|
255
256
|
REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
256
257
|
{
|
|
258
|
+
NoRedundantLocalAlias.id: NoRedundantLocalAlias,
|
|
257
259
|
PytestFixtureReturnsClosedResource.id: PytestFixtureReturnsClosedResource,
|
|
258
260
|
DuplicatePytestFixtureParams.id: DuplicatePytestFixtureParams,
|
|
259
261
|
PreferTypedSettingsField.id: PreferTypedSettingsField,
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from pathlib import PurePosixPath
|
|
6
|
+
import re
|
|
7
|
+
from typing import TYPE_CHECKING, ClassVar, final, override
|
|
8
|
+
import unicodedata
|
|
9
|
+
|
|
10
|
+
from sarj_python_lint.rule_base import (
|
|
11
|
+
AutofixPolicy,
|
|
12
|
+
Diagnostic,
|
|
13
|
+
ExampleFile,
|
|
14
|
+
ExampleOutcome,
|
|
15
|
+
Rule,
|
|
16
|
+
RuleCategory,
|
|
17
|
+
RuleDocumentation,
|
|
18
|
+
RuleExample,
|
|
19
|
+
Severity,
|
|
20
|
+
)
|
|
21
|
+
from sarj_python_lint.rules._ast_index import children
|
|
22
|
+
from sarj_python_lint.rules._suppression_comments import comment_lines
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Mapping
|
|
27
|
+
|
|
28
|
+
from sarj_python_lint._file_context import PythonFileContext
|
|
29
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_FUNCTIONS = (ast.FunctionDef, ast.AsyncFunctionDef)
|
|
33
|
+
_SUITES = (*_FUNCTIONS, ast.With, ast.AsyncWith)
|
|
34
|
+
_UNCERTAIN = (
|
|
35
|
+
*_FUNCTIONS,
|
|
36
|
+
ast.ClassDef,
|
|
37
|
+
ast.Lambda,
|
|
38
|
+
ast.ListComp,
|
|
39
|
+
ast.SetComp,
|
|
40
|
+
ast.DictComp,
|
|
41
|
+
ast.GeneratorExp,
|
|
42
|
+
ast.If,
|
|
43
|
+
ast.For,
|
|
44
|
+
ast.AsyncFor,
|
|
45
|
+
ast.While,
|
|
46
|
+
ast.Try,
|
|
47
|
+
ast.TryStar,
|
|
48
|
+
ast.Match,
|
|
49
|
+
ast.Assert,
|
|
50
|
+
ast.BoolOp,
|
|
51
|
+
ast.Global,
|
|
52
|
+
ast.Nonlocal,
|
|
53
|
+
ast.TypeAlias,
|
|
54
|
+
)
|
|
55
|
+
_INITIALIZERS = (ast.Call, ast.Await, ast.Constant, ast.List, ast.Tuple, ast.Dict, ast.Set)
|
|
56
|
+
_REFERENCE_OBSERVERS = frozenset(
|
|
57
|
+
{
|
|
58
|
+
"getrefcount",
|
|
59
|
+
"get_referrers",
|
|
60
|
+
"getattr",
|
|
61
|
+
"attrgetter",
|
|
62
|
+
"TypeAdapter",
|
|
63
|
+
"BaseModel",
|
|
64
|
+
"RootModel",
|
|
65
|
+
"create_model",
|
|
66
|
+
"validate_call",
|
|
67
|
+
"model_rebuild",
|
|
68
|
+
"rebuild_dataclass",
|
|
69
|
+
"new_class",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
_WORDS = re.compile(r"\w+")
|
|
73
|
+
_TYPE_COMMENT = re.compile(r"#\s*type\s*:")
|
|
74
|
+
_NAME_PAIR = re.compile(r"\b(?P<target>[A-Za-z_]\w*)[\s)\\]*=[\s(\\]*(?P<source>[A-Za-z_]\w*)\b(?=[\s),;\\]|$)")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@final
|
|
78
|
+
class NoRedundantLocalAlias(Rule):
|
|
79
|
+
id = "no-redundant-local-alias"
|
|
80
|
+
code = "SARJ531"
|
|
81
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
82
|
+
default_level=Severity.WARNING,
|
|
83
|
+
summary="Remove a local alias used only for its matching keyword argument.",
|
|
84
|
+
rationale="An extra local binding makes readers trace two names for one value when the keyword already communicates its role.",
|
|
85
|
+
remediation="Pass the original local directly to the matching keyword and remove the unannotated alias assignment after review.",
|
|
86
|
+
category=RuleCategory.MAINTAINABILITY,
|
|
87
|
+
autofix=AutofixPolicy.NONE,
|
|
88
|
+
limitations=(
|
|
89
|
+
"Only unannotated, single-target `alias = original` assignments with one binding and one read at `alias=alias` are checked.",
|
|
90
|
+
"The original must be a parameter or an earlier initialized local with one binding; reassignment, deletion, imports, alias chains and uncertain initialization are excluded.",
|
|
91
|
+
"Functions with if statements, loops, try statements, asserts, Boolean short-circuiting, nested scopes, global/nonlocal declarations or type aliases are excluded. Ordinary and async with bodies require ordered enclosing-suite binding evidence.",
|
|
92
|
+
"A conditional expression is admitted only when its test is a bare name bound once as a parameter, and neither local is read within the expression.",
|
|
93
|
+
"Typed aliases, files with type comments, same-scope quoted references and conditional reads of either local are excluded. Captured bindings and module/class aliases are preserved.",
|
|
94
|
+
"Files with wildcard imports, Pydantic imports or references to known namespace readers, dynamic attribute getters or reference-count observers are excluded. Wildcard exports cannot establish builtin identity; computed type names can consume caller-local bindings without a Name read in the AST.",
|
|
95
|
+
"Dynamic class construction and class-valued references to type are excluded. Direct, unshadowed unary type(value) queries and standard-library dataclasses remain supported.",
|
|
96
|
+
"Generated source, stubs and malformed input are excluded. Arbitrary external frame introspection is not modeled.",
|
|
97
|
+
"No autofix is offered; readability judgments, multiple reads, positional forwarding and more complex control flow remain outside the contract.",
|
|
98
|
+
),
|
|
99
|
+
examples=(
|
|
100
|
+
RuleExample(
|
|
101
|
+
example_id="keyword-supplies-role",
|
|
102
|
+
title="The keyword supplies the role",
|
|
103
|
+
outcome=ExampleOutcome.MATCH,
|
|
104
|
+
files=(
|
|
105
|
+
ExampleFile.python(
|
|
106
|
+
"case.py", "def render(item):\n payload = item\n consume(payload=payload)\n"
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
focus_path=PurePosixPath("case.py"),
|
|
110
|
+
expected_count=1,
|
|
111
|
+
public=True,
|
|
112
|
+
),
|
|
113
|
+
RuleExample(
|
|
114
|
+
example_id="direct-keyword",
|
|
115
|
+
title="Pass the original local",
|
|
116
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
117
|
+
files=(ExampleFile.python("case.py", "def render(item):\n consume(payload=item)\n"),),
|
|
118
|
+
focus_path=PurePosixPath("case.py"),
|
|
119
|
+
expected_count=0,
|
|
120
|
+
public=True,
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
description = documentation.summary
|
|
125
|
+
|
|
126
|
+
@override
|
|
127
|
+
def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
|
|
128
|
+
if _excluded_file(context):
|
|
129
|
+
return []
|
|
130
|
+
keywords = _matching_keywords(context)
|
|
131
|
+
candidates = _local_assignments(context, set(keywords.values()))
|
|
132
|
+
if not candidates:
|
|
133
|
+
return []
|
|
134
|
+
provenance = context.resource_provenance
|
|
135
|
+
names = {node.targets[0].id for node in candidates if isinstance(node.targets[0], ast.Name)}
|
|
136
|
+
reads = _scope_reads(provenance, names)
|
|
137
|
+
conditional_sources = _conditional_sources(context)
|
|
138
|
+
quoted = _quoted_references(context, provenance, names)
|
|
139
|
+
positions = {statement: i for owner in context.nodes(*_SUITES) for i, statement in enumerate(owner.body)}
|
|
140
|
+
findings: list[Diagnostic] = []
|
|
141
|
+
for assignment in candidates:
|
|
142
|
+
target = assignment.targets[0]
|
|
143
|
+
source = assignment.value
|
|
144
|
+
if not isinstance(target, ast.Name) or not isinstance(source, ast.Name):
|
|
145
|
+
continue
|
|
146
|
+
if (provenance.scope(assignment), target.id) in quoted:
|
|
147
|
+
continue
|
|
148
|
+
use = _single_keyword_read(target, provenance, reads, keywords)
|
|
149
|
+
if use is None:
|
|
150
|
+
continue
|
|
151
|
+
if not _dominates(assignment, use, context.parents, positions):
|
|
152
|
+
continue
|
|
153
|
+
if not _stable_source(source, assignment, provenance, positions, conditional_sources):
|
|
154
|
+
continue
|
|
155
|
+
findings.append(
|
|
156
|
+
Diagnostic(
|
|
157
|
+
path=context.path,
|
|
158
|
+
line=target.lineno,
|
|
159
|
+
col=target.col_offset + 1,
|
|
160
|
+
code=self.code,
|
|
161
|
+
message=f"Local `{target.id}` only forwards `{source.id}` to `{target.id}=`; pass `{source.id}` directly and remove the alias.",
|
|
162
|
+
severity=Severity.WARNING,
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
return findings
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _excluded_file(context: PythonFileContext) -> bool:
|
|
169
|
+
if context.path.suffix != ".py" or "=" not in context.source:
|
|
170
|
+
return True
|
|
171
|
+
# This is only a necessary ASCII signal; Unicode identifiers use the AST.
|
|
172
|
+
if context.source.isascii() and not _possible_alias(context.source):
|
|
173
|
+
return True
|
|
174
|
+
return context.tree is None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _possible_alias(source: str) -> bool:
|
|
178
|
+
# Comments may separate tokens; bypass the text gate instead of rescanning them.
|
|
179
|
+
if "#" in source:
|
|
180
|
+
return True
|
|
181
|
+
forwarded: set[str] = set()
|
|
182
|
+
assigned: set[str] = set()
|
|
183
|
+
for match in _NAME_PAIR.finditer(source):
|
|
184
|
+
target = match.group("target")
|
|
185
|
+
(forwarded if target == match.group("source") else assigned).add(target)
|
|
186
|
+
return not forwarded.isdisjoint(assigned)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _matching_keywords(context: PythonFileContext) -> dict[ast.Name, str]:
|
|
190
|
+
return {
|
|
191
|
+
node.value: node.value.id
|
|
192
|
+
for node in context.nodes(ast.keyword)
|
|
193
|
+
if isinstance(node.value, ast.Name) and node.arg == node.value.id
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _local_assignments(context: PythonFileContext, names: set[str]) -> list[ast.Assign]:
|
|
198
|
+
candidates = [
|
|
199
|
+
node
|
|
200
|
+
for node in context.nodes(ast.Assign)
|
|
201
|
+
if len(node.targets) == 1
|
|
202
|
+
and isinstance(node.targets[0], ast.Name)
|
|
203
|
+
and node.targets[0].id in names
|
|
204
|
+
and isinstance(node.value, ast.Name)
|
|
205
|
+
and node.targets[0].id != node.value.id
|
|
206
|
+
]
|
|
207
|
+
if not candidates or context.has_wildcard_import or context.dynamic_namespace or _observes_references(context):
|
|
208
|
+
return []
|
|
209
|
+
provenance = context.resource_provenance
|
|
210
|
+
blocked = {provenance.scope(node) for node in context.nodes(*_UNCERTAIN)}
|
|
211
|
+
blocked.update(
|
|
212
|
+
provenance.scope(node) for node in context.nodes(ast.IfExp) if not _parameter_choice(node, provenance)
|
|
213
|
+
)
|
|
214
|
+
eligible = [
|
|
215
|
+
node for node in candidates if isinstance(scope := provenance.scope(node), _FUNCTIONS) and scope not in blocked
|
|
216
|
+
]
|
|
217
|
+
if not eligible or context.generated or _has_type_comments(context):
|
|
218
|
+
return []
|
|
219
|
+
return eligible
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _has_type_comments(context: PythonFileContext) -> bool:
|
|
223
|
+
return any(_TYPE_COMMENT.search(comment) for comment in comment_lines(tuple(context.source_lines)).values())
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _parameter_choice(node: ast.IfExp, provenance: ResourceProvenance) -> bool:
|
|
227
|
+
if not isinstance(node.test, ast.Name):
|
|
228
|
+
return False
|
|
229
|
+
bindings = provenance.bindings.get(provenance.scope(node), {}).get(node.test.id, ())
|
|
230
|
+
return len(bindings) == 1 and isinstance(bindings[0], ast.arg)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _scope_reads(provenance: ResourceProvenance, names: set[str]) -> dict[tuple[ast.AST | None, str], list[ast.Name]]:
|
|
234
|
+
reads: dict[tuple[ast.AST | None, str], list[ast.Name]] = defaultdict(list)
|
|
235
|
+
for name in names:
|
|
236
|
+
for read in provenance.reads.get(name, ()):
|
|
237
|
+
reads[provenance.scope(read), name].append(read)
|
|
238
|
+
return reads
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _conditional_sources(context: PythonFileContext) -> set[tuple[ast.AST | None, str]]:
|
|
242
|
+
provenance = context.resource_provenance
|
|
243
|
+
pending: list[ast.AST] = list(context.nodes(ast.IfExp))
|
|
244
|
+
seen: set[ast.AST] = set()
|
|
245
|
+
names: set[tuple[ast.AST | None, str]] = set()
|
|
246
|
+
while pending:
|
|
247
|
+
node = pending.pop()
|
|
248
|
+
if node in seen:
|
|
249
|
+
continue
|
|
250
|
+
seen.add(node)
|
|
251
|
+
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
|
|
252
|
+
names.add((provenance.scope(node), node.id))
|
|
253
|
+
pending.extend(children(node))
|
|
254
|
+
return names
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _single_keyword_read(
|
|
258
|
+
target: ast.Name,
|
|
259
|
+
provenance: ResourceProvenance,
|
|
260
|
+
reads: Mapping[tuple[ast.AST | None, str], list[ast.Name]],
|
|
261
|
+
keywords: Mapping[ast.Name, str],
|
|
262
|
+
) -> ast.Name | None:
|
|
263
|
+
scope = provenance.scope(target)
|
|
264
|
+
uses = reads.get((scope, target.id), ())
|
|
265
|
+
if provenance.bindings.get(scope, {}).get(target.id) != [target] or len(uses) != 1:
|
|
266
|
+
return None
|
|
267
|
+
[use] = uses
|
|
268
|
+
if keywords.get(use) != target.id or _statement(use, provenance.parents) is None:
|
|
269
|
+
return None
|
|
270
|
+
return use
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _quoted_references(
|
|
274
|
+
context: PythonFileContext,
|
|
275
|
+
provenance: ResourceProvenance,
|
|
276
|
+
names: set[str],
|
|
277
|
+
) -> set[tuple[ast.AST | None, str]]:
|
|
278
|
+
references: set[tuple[ast.AST | None, str]] = set()
|
|
279
|
+
for node in context.nodes(ast.Constant):
|
|
280
|
+
if not isinstance(node.value, str):
|
|
281
|
+
continue
|
|
282
|
+
mentioned = _quoted_words(node.value) & names
|
|
283
|
+
if mentioned:
|
|
284
|
+
scope = provenance.scope(node)
|
|
285
|
+
references.update((scope, name) for name in mentioned)
|
|
286
|
+
return references
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _quoted_words(value: str) -> set[str]:
|
|
290
|
+
normalized = unicodedata.normalize("NFKC", value)
|
|
291
|
+
if normalized.isascii():
|
|
292
|
+
return set(_WORDS.findall(normalized))
|
|
293
|
+
words: set[str] = set()
|
|
294
|
+
word: list[str] = []
|
|
295
|
+
for character in normalized:
|
|
296
|
+
# A leading letter tests identifier continuation, including combining marks.
|
|
297
|
+
if ("a" + character).isidentifier():
|
|
298
|
+
word.append(character)
|
|
299
|
+
elif word:
|
|
300
|
+
words.add("".join(word))
|
|
301
|
+
word.clear()
|
|
302
|
+
if word:
|
|
303
|
+
words.add("".join(word))
|
|
304
|
+
return words
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _observes_references(context: PythonFileContext) -> bool:
|
|
308
|
+
if _imports_model_namespace(context):
|
|
309
|
+
return True
|
|
310
|
+
for node in context.nodes(ast.Name, ast.Attribute, ast.alias):
|
|
311
|
+
name = _reference_name(node)
|
|
312
|
+
if name in _REFERENCE_OBSERVERS or (name == "type" and not _plain_type_query(node, context)):
|
|
313
|
+
return True
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _imports_model_namespace(context: PythonFileContext) -> bool:
|
|
318
|
+
for node in context.nodes(ast.Import, ast.ImportFrom):
|
|
319
|
+
if isinstance(node, ast.ImportFrom):
|
|
320
|
+
if node.level == 0 and (node.module or "").partition(".")[0] == "pydantic":
|
|
321
|
+
return True
|
|
322
|
+
elif any(alias.name.partition(".")[0] == "pydantic" for alias in node.names):
|
|
323
|
+
return True
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _reference_name(node: ast.AST) -> str | None:
|
|
328
|
+
if isinstance(node, ast.Name):
|
|
329
|
+
return node.id
|
|
330
|
+
if isinstance(node, ast.Attribute):
|
|
331
|
+
return node.attr
|
|
332
|
+
return node.name if isinstance(node, ast.alias) else None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _plain_type_query(node: ast.AST, context: PythonFileContext) -> bool:
|
|
336
|
+
parent = context.parents.get(node)
|
|
337
|
+
return (
|
|
338
|
+
isinstance(node, ast.Name)
|
|
339
|
+
and context.imports.builtin_is_unshadowed("type")
|
|
340
|
+
and isinstance(parent, ast.Call)
|
|
341
|
+
and parent.func is node
|
|
342
|
+
and len(parent.args) == 1
|
|
343
|
+
and not parent.keywords
|
|
344
|
+
and not isinstance(parent.args[0], ast.Starred)
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _statement(node: ast.AST, parents: Mapping[ast.AST, ast.AST]) -> ast.stmt | None:
|
|
349
|
+
while not isinstance(node, ast.stmt):
|
|
350
|
+
if isinstance(node, (ast.IfExp, ast.BoolOp)) or (parent := parents.get(node)) is None:
|
|
351
|
+
return None
|
|
352
|
+
node = parent
|
|
353
|
+
return node
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _dominates(
|
|
357
|
+
before: ast.stmt,
|
|
358
|
+
at: ast.AST,
|
|
359
|
+
parents: Mapping[ast.AST, ast.AST],
|
|
360
|
+
positions: Mapping[ast.stmt, int],
|
|
361
|
+
) -> bool:
|
|
362
|
+
owner = parents.get(before)
|
|
363
|
+
if before not in positions or not isinstance(owner, _SUITES):
|
|
364
|
+
return False
|
|
365
|
+
current = at
|
|
366
|
+
while parents.get(current) is not owner:
|
|
367
|
+
parent = parents.get(current)
|
|
368
|
+
if parent is None:
|
|
369
|
+
return False
|
|
370
|
+
current = parent
|
|
371
|
+
# Exiting a with body can suppress an exception and skip its assignments.
|
|
372
|
+
# Only the same suite or an earlier enclosing-suite statement dominates.
|
|
373
|
+
return isinstance(current, ast.stmt) and current in positions and positions[before] < positions[current]
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _stable_source(
|
|
377
|
+
source: ast.Name,
|
|
378
|
+
at: ast.Assign,
|
|
379
|
+
provenance: ResourceProvenance,
|
|
380
|
+
positions: Mapping[ast.stmt, int],
|
|
381
|
+
conditional_sources: set[tuple[ast.AST | None, str]],
|
|
382
|
+
) -> bool:
|
|
383
|
+
scope = provenance.scope(at)
|
|
384
|
+
if (scope, source.id) in conditional_sources:
|
|
385
|
+
return False
|
|
386
|
+
bindings = provenance.bindings.get(scope, {}).get(source.id, ())
|
|
387
|
+
if len(bindings) != 1:
|
|
388
|
+
return False
|
|
389
|
+
[binding] = bindings
|
|
390
|
+
if isinstance(binding, ast.arg):
|
|
391
|
+
return True
|
|
392
|
+
parent = provenance.parents.get(binding)
|
|
393
|
+
if not isinstance(binding, ast.Name) or not isinstance(parent, ast.Assign | ast.AnnAssign):
|
|
394
|
+
return False
|
|
395
|
+
if isinstance(parent, ast.Assign) and (len(parent.targets) != 1 or parent.targets[0] is not binding):
|
|
396
|
+
return False
|
|
397
|
+
return isinstance(parent.value, _INITIALIZERS) and _dominates(parent, at, provenance.parents, positions)
|
|
@@ -216,6 +216,10 @@ class NoUnnecessaryDocstring(ProjectRule):
|
|
|
216
216
|
tree = context.tree
|
|
217
217
|
if tree is None:
|
|
218
218
|
return []
|
|
219
|
+
if not any(
|
|
220
|
+
docstring_expression(owner) is not None for owner in _docstring_owners(tree, node_index=context.node_index)
|
|
221
|
+
):
|
|
222
|
+
return []
|
|
219
223
|
metadata = _metadata_context_bindings(context, tree)
|
|
220
224
|
owner_keys = _owner_keys(tree)
|
|
221
225
|
explicit_consumers = _explicit_docstring_consumers(
|
|
@@ -175,11 +175,12 @@ class NoWholeRequestResponsePayloadInLog(Rule):
|
|
|
175
175
|
tree = context.tree
|
|
176
176
|
if tree is None:
|
|
177
177
|
return []
|
|
178
|
+
calls = [node for node in context.nodes(ast.Call) if _is_logging_call(node)]
|
|
179
|
+
if not calls:
|
|
180
|
+
return []
|
|
178
181
|
facts = _analysis_facts(context)
|
|
179
182
|
diagnostics: list[Diagnostic] = []
|
|
180
|
-
for node in
|
|
181
|
-
if not _is_logging_call(node):
|
|
182
|
-
continue
|
|
183
|
+
for node in calls:
|
|
183
184
|
statement = _containing_statement(node, facts.parents)
|
|
184
185
|
suite_id = facts.statement_suites.get(id(statement)) if statement is not None else None
|
|
185
186
|
suite = facts.suites.get(suite_id) if suite_id is not None else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/_analysis_session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/interpreter_argv.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_names.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_ast_position.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_closed_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_copy_on_write.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_dict_key_views.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_local_source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_namespace_access.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_nominal_project.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_route_shapes.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_runtime_bindings.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_set_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_static_json_alias.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_store_writes.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_assertions.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_composition.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.69.1 → sarj_python_lint-1.70.1}/src/sarj_python_lint/rules/_test_provenance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|