sarj-python-lint 1.3.1__tar.gz → 1.3.3__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.3.1 → sarj_python_lint-1.3.3}/PKG-INFO +1 -1
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/pyproject.toml +1 -1
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +2 -2
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +109 -5
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/.gitignore +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/LICENSE +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/README.md +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
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/
|
|
@@ -272,11 +272,11 @@ class _NameUsage(ast.NodeVisitor):
|
|
|
272
272
|
|
|
273
273
|
@override
|
|
274
274
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
275
|
-
|
|
275
|
+
"""Skip the deferred lambda body when tracking immediate name usage."""
|
|
276
276
|
|
|
277
277
|
@override
|
|
278
278
|
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None:
|
|
279
|
-
|
|
279
|
+
"""Skip the deferred generator body when tracking immediate name usage."""
|
|
280
280
|
|
|
281
281
|
def visit_Import(self, node: ast.Import) -> None:
|
|
282
282
|
self.rebound |= any((alias.asname or alias.name.split(".", maxsplit=1)[0]) == self.name for alias in node.names)
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
from collections import Counter
|
|
5
|
-
from dataclasses import dataclass
|
|
5
|
+
from dataclasses import dataclass, replace
|
|
6
6
|
from pathlib import PurePosixPath
|
|
7
7
|
from typing import TYPE_CHECKING, ClassVar, NamedTuple, final, override
|
|
8
8
|
|
|
@@ -67,9 +67,16 @@ class _WrapperSummary:
|
|
|
67
67
|
accessors: dict[str, str]
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
@dataclass(frozen=True, slots=True)
|
|
71
|
+
class _WrapperFactorySummary:
|
|
72
|
+
wrapper: _WrapperSummary
|
|
73
|
+
accessor_origins: dict[str, frozenset[ast.Call]]
|
|
74
|
+
|
|
75
|
+
|
|
70
76
|
@dataclass(frozen=True, slots=True)
|
|
71
77
|
class _ModuleSummaries:
|
|
72
78
|
wrappers: dict[str, _WrapperSummary]
|
|
79
|
+
wrapper_factories: dict[str, _WrapperFactorySummary]
|
|
73
80
|
decoder_parameters: dict[str, int]
|
|
74
81
|
record_parameters: dict[str, frozenset[int]]
|
|
75
82
|
local_parameters: dict[str, frozenset[int]]
|
|
@@ -122,7 +129,8 @@ class RequirePydanticForExternalJson(Rule):
|
|
|
122
129
|
autofix=AutofixPolicy.NONE,
|
|
123
130
|
limitations=(
|
|
124
131
|
"The rule follows import-proven JSON decoders, requests/httpx/aiohttp responses and clients, environment or subprocess results, and simple module-local helpers through single-assignment names.",
|
|
125
|
-
"Simple same-module wrappers preserve constructor arguments through direct stored-field accessor methods, including local identity-or-empty-dict narrowing helpers; inheritance, decorators, mutation, nested captures, unknown member escapes
|
|
132
|
+
"Simple same-module wrappers preserve constructor arguments through direct stored-field accessor methods, including local identity-or-empty-dict narrowing helpers; inheritance, decorators, mutation, nested captures, unknown member escapes, and unknown helpers are excluded.",
|
|
133
|
+
"No-argument synchronous module factories with an optional docstring and a single direct, fully bound wrapper-constructor return preserve accessor provenance; annotations alone, parameterized/async/method factories, returned aliases, and factory chains are not inferred.",
|
|
126
134
|
"It diagnoses literal-key subscription and get calls; iteration and other dynamic JSON use remain out of scope.",
|
|
127
135
|
"Unannotated parameters and unknown expressions are not assumed to be external; interprocedural and framework-specific boundaries can remain unreported.",
|
|
128
136
|
"Names bound by loops, comprehensions, context managers, exception handlers, pattern matching, imports, or destructuring are excluded when their provenance is ambiguous; unrelated binders do not suppress the rest of a function.",
|
|
@@ -179,7 +187,8 @@ class RequirePydanticForExternalJson(Rule):
|
|
|
179
187
|
|
|
180
188
|
first_by_origin: dict[int, tuple[ast.expr, ast.Call]] = {}
|
|
181
189
|
for sink, origin in sorted(findings, key=lambda item: (item[0].lineno, item[0].col_offset)):
|
|
182
|
-
|
|
190
|
+
if not is_suppressed(source_lines, sink.lineno, self.code):
|
|
191
|
+
first_by_origin.setdefault(id(origin), (sink, origin))
|
|
183
192
|
return [
|
|
184
193
|
Diagnostic(
|
|
185
194
|
path=path,
|
|
@@ -194,7 +203,6 @@ class RequirePydanticForExternalJson(Rule):
|
|
|
194
203
|
severity=Severity.WARNING,
|
|
195
204
|
)
|
|
196
205
|
for sink, _origin in first_by_origin.values()
|
|
197
|
-
if not is_suppressed(source_lines, sink.lineno, self.code)
|
|
198
206
|
]
|
|
199
207
|
|
|
200
208
|
|
|
@@ -228,8 +236,9 @@ def _module_summaries(
|
|
|
228
236
|
continue
|
|
229
237
|
_summarize_function(function, imports, module_validator_names, source_lines, decoders, records=records)
|
|
230
238
|
response_callables = _response_callables(tree, imports, node_index=node_index)
|
|
231
|
-
|
|
239
|
+
summaries = _ModuleSummaries(
|
|
232
240
|
_wrapper_summaries(tree, imports),
|
|
241
|
+
{},
|
|
233
242
|
decoders,
|
|
234
243
|
records,
|
|
235
244
|
_locally_sourced_parameters(functions),
|
|
@@ -244,6 +253,7 @@ def _module_summaries(
|
|
|
244
253
|
_pydantic_adapter_names(tree, imports, binding_counts),
|
|
245
254
|
_pydantic_model_names(tree, imports, binding_counts),
|
|
246
255
|
)
|
|
256
|
+
return replace(summaries, wrapper_factories=_wrapper_factory_summaries(tree, imports, summaries))
|
|
247
257
|
|
|
248
258
|
|
|
249
259
|
def _response_callables(
|
|
@@ -777,6 +787,9 @@ class _OriginResolver:
|
|
|
777
787
|
|
|
778
788
|
def _call_origins(self, call: ast.Call, resolving: frozenset[str]) -> frozenset[ast.Call]:
|
|
779
789
|
if isinstance(call.func, ast.Attribute) and not call.args and not call.keywords:
|
|
790
|
+
factory_origins = self._factory_accessor_origins(call.func)
|
|
791
|
+
if factory_origins is not None:
|
|
792
|
+
return factory_origins
|
|
780
793
|
wrapped = self._wrapper_value(call.func)
|
|
781
794
|
if wrapped is not None:
|
|
782
795
|
return self.origins(wrapped, resolving)
|
|
@@ -799,6 +812,31 @@ class _OriginResolver:
|
|
|
799
812
|
found_origins.update(self.origins(keyword.value, resolving))
|
|
800
813
|
return frozenset(found_origins)
|
|
801
814
|
|
|
815
|
+
def factory_argument_origins(self, value: ast.expr, narrowing_helpers: frozenset[str]) -> frozenset[ast.Call]:
|
|
816
|
+
if not isinstance(value, ast.Call) or self._is_validation_call(value):
|
|
817
|
+
return frozenset()
|
|
818
|
+
if self._is_source(value):
|
|
819
|
+
return frozenset({value})
|
|
820
|
+
if (
|
|
821
|
+
isinstance(value.func, ast.Name)
|
|
822
|
+
and value.func.id in narrowing_helpers
|
|
823
|
+
and len(value.args) == 1
|
|
824
|
+
and not value.keywords
|
|
825
|
+
):
|
|
826
|
+
return self.factory_argument_origins(value.args[0], narrowing_helpers)
|
|
827
|
+
return frozenset()
|
|
828
|
+
|
|
829
|
+
def _factory_accessor_origins(self, access: ast.Attribute) -> frozenset[ast.Call] | None:
|
|
830
|
+
receiver = self._resolved_binding(access.value) if isinstance(access.value, ast.Name) else access.value
|
|
831
|
+
if not isinstance(receiver, ast.Call) or not isinstance(receiver.func, ast.Name):
|
|
832
|
+
return None
|
|
833
|
+
if receiver.args or receiver.keywords or receiver.func.id in self.local_bound_names:
|
|
834
|
+
return None
|
|
835
|
+
factory = self.summaries.wrapper_factories.get(receiver.func.id)
|
|
836
|
+
if factory is None or not self._wrapper_is_stable(receiver, factory.wrapper):
|
|
837
|
+
return None
|
|
838
|
+
return factory.accessor_origins.get(access.attr, frozenset())
|
|
839
|
+
|
|
802
840
|
def _wrapper_value(self, access: ast.Attribute) -> ast.expr | None:
|
|
803
841
|
receiver = self._resolved_binding(access.value) if isinstance(access.value, ast.Name) else access.value
|
|
804
842
|
if not isinstance(receiver, ast.Call) or not isinstance(receiver.func, ast.Name):
|
|
@@ -1691,3 +1729,69 @@ def _wrapper_descriptor(statement: ast.stmt) -> bool:
|
|
|
1691
1729
|
if isinstance(statement, ast.Assign):
|
|
1692
1730
|
return not all(isinstance(target, ast.Name) and target.id == "__slots__" for target in statement.targets)
|
|
1693
1731
|
return isinstance(statement, ast.AnnAssign | ast.AugAssign)
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
def _wrapper_factory_summaries(
|
|
1735
|
+
tree: ast.Module, imports: ImportIndex, summaries: _ModuleSummaries
|
|
1736
|
+
) -> dict[str, _WrapperFactorySummary]:
|
|
1737
|
+
counts = _suite_binding_counts(tree.body)
|
|
1738
|
+
narrowing_helpers = _narrowing_helpers(tree, imports, counts)
|
|
1739
|
+
factories: dict[str, _WrapperFactorySummary] = {}
|
|
1740
|
+
for function in tree.body:
|
|
1741
|
+
if not isinstance(function, ast.FunctionDef) or counts[function.name] != 1:
|
|
1742
|
+
continue
|
|
1743
|
+
returned = _direct_factory_return(function)
|
|
1744
|
+
if returned is None or not isinstance(returned.func, ast.Name):
|
|
1745
|
+
continue
|
|
1746
|
+
wrapper = summaries.wrappers.get(returned.func.id)
|
|
1747
|
+
if wrapper is None:
|
|
1748
|
+
continue
|
|
1749
|
+
arguments = _factory_constructor_arguments(returned, wrapper)
|
|
1750
|
+
if arguments is None:
|
|
1751
|
+
continue
|
|
1752
|
+
scope = _own_scope(function)
|
|
1753
|
+
resolver = _OriginResolver(
|
|
1754
|
+
imports,
|
|
1755
|
+
summaries,
|
|
1756
|
+
frozenset(),
|
|
1757
|
+
frozenset(),
|
|
1758
|
+
frozenset(),
|
|
1759
|
+
{},
|
|
1760
|
+
_function_local_bound_names(function, scope),
|
|
1761
|
+
scope,
|
|
1762
|
+
)
|
|
1763
|
+
origins = {
|
|
1764
|
+
accessor: resolver.factory_argument_origins(arguments[wrapper.fields[field]], narrowing_helpers)
|
|
1765
|
+
for accessor, field in wrapper.accessors.items()
|
|
1766
|
+
}
|
|
1767
|
+
if any(origins.values()):
|
|
1768
|
+
factories[function.name] = _WrapperFactorySummary(wrapper, origins)
|
|
1769
|
+
return factories
|
|
1770
|
+
|
|
1771
|
+
|
|
1772
|
+
def _direct_factory_return(function: ast.FunctionDef) -> ast.Call | None:
|
|
1773
|
+
statements = function.body[1:] if ast.get_docstring(function, clean=False) is not None else function.body
|
|
1774
|
+
if (
|
|
1775
|
+
function.decorator_list
|
|
1776
|
+
or _parameter_positions(function)
|
|
1777
|
+
or any((function.args.kwonlyargs, function.args.vararg, function.args.kwarg, function.type_params))
|
|
1778
|
+
or len(statements) != 1
|
|
1779
|
+
):
|
|
1780
|
+
return None
|
|
1781
|
+
statement = statements[0]
|
|
1782
|
+
if not isinstance(statement, ast.Return) or not isinstance(statement.value, ast.Call):
|
|
1783
|
+
return None
|
|
1784
|
+
if any(isinstance(node, ast.NamedExpr | ast.Yield | ast.YieldFrom | ast.Await) for node in ast.walk(statement)):
|
|
1785
|
+
return None
|
|
1786
|
+
return statement.value
|
|
1787
|
+
|
|
1788
|
+
|
|
1789
|
+
def _factory_constructor_arguments(call: ast.Call, wrapper: _WrapperSummary) -> dict[str, ast.expr] | None:
|
|
1790
|
+
if len(call.args) > len(wrapper.parameters) or any(isinstance(argument, ast.Starred) for argument in call.args):
|
|
1791
|
+
return None
|
|
1792
|
+
arguments = dict(zip(wrapper.parameters, call.args, strict=False))
|
|
1793
|
+
for keyword in call.keywords:
|
|
1794
|
+
if keyword.arg not in wrapper.parameters or keyword.arg in arguments:
|
|
1795
|
+
return None
|
|
1796
|
+
arguments[keyword.arg] = keyword.value
|
|
1797
|
+
return arguments if len(arguments) == len(wrapper.parameters) else None
|
|
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
|
|
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.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_ast_position.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_nominal_project.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_test_assertions.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/_test_composition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/excessive_commentary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_any_mapping_types.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_delete_statement.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_dunder_all.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_fastapi_on_event.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
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_offset_pagination.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
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_restated_comment.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/no_select_star.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
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/phase_label_comment.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_class_row.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_library_fake.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
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/prefer_str_enum.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
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/redundant_docstring.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/typed_error_reasons.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.3.1 → sarj_python_lint-1.3.3}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|