sarj-python-lint 1.108.1__tar.gz → 1.108.51__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.108.1 → sarj_python_lint-1.108.51}/PKG-INFO +2 -2
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/pyproject.toml +2 -2
- sarj_python_lint-1.108.51/src/sarj_python_lint/rules/_loguru.py +17 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_resource_provenance.py +9 -1
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_async_function_cache.py +6 -1
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_loguru_exc_info.py +33 -14
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_loguru_printf_format.py +2 -9
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_secret_in_log.py +90 -16
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +63 -5
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +2 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/.gitignore +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/LICENSE +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/README.md +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_aliases.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_annotations.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_comments.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_docs.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_enums.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_inert.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_parameters.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_repeated.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_security.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_tests.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_fixes.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_parallel.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_prepared_sources.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_rule_descriptors.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/application_logging.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/autofix.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/cast_access.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rule_metadata.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_ast_names.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_binding_inventory.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_native_object_contracts.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_postgres.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_postgres_ctas.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_runtime_names.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_runtime_registry.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_store_sql.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_async_client_in_constructor.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_custom_control_flow_exception.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_discarded_tagged_outcome.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_dynamic_builtins.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_generic_json_parser.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_mutating_input_with_result.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_recording_store.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_redundant_attribute_unpacking.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_redundant_local_alias.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_redundant_sql_alias.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_shared_mutable_default.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_sys_path_mutation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_test_comments.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_application_model_joins.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_optional_composite.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_pytest_fixture_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_runtime_validation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_typed_cli.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_constrained_api_integer.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/service_singleton_delegates_to_bulk.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/store_insert_requires_explicit_conflict_policy.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/store_mutation_command_fanout.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
- {sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/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.108.
|
|
3
|
+
Version: 1.108.51
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
|
|
6
6
|
Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
|
|
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
|
|
|
19
19
|
Requires-Python: >=3.14
|
|
20
20
|
Requires-Dist: packaging==26.3
|
|
21
21
|
Requires-Dist: ruff>=0.17.2
|
|
22
|
-
Requires-Dist: sarj-rule-contracts==0.6.
|
|
22
|
+
Requires-Dist: sarj-rule-contracts==0.6.2
|
|
23
23
|
Requires-Dist: sqlglot==30.24.0
|
|
24
24
|
Requires-Dist: typer==0.27.3
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sarj-python-lint"
|
|
3
|
-
version = "1.108.
|
|
3
|
+
version = "1.108.51"
|
|
4
4
|
description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "sarj-ai" }]
|
|
@@ -15,7 +15,7 @@ classifiers = [
|
|
|
15
15
|
"Programming Language :: Python :: 3.14",
|
|
16
16
|
"Topic :: Software Development :: Quality Assurance",
|
|
17
17
|
]
|
|
18
|
-
dependencies = ["sarj-rule-contracts==0.6.
|
|
18
|
+
dependencies = ["sarj-rule-contracts==0.6.2", "packaging==26.3", "ruff>=0.17.2", "sqlglot==30.24.0", "typer==0.27.3"]
|
|
19
19
|
|
|
20
20
|
[project.scripts]
|
|
21
21
|
sarj-python-lint = "sarj_python_lint.__main__:main"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from sarj_python_lint._file_context import PythonFileContext
|
|
9
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def logger_imports_untouched(context: PythonFileContext, provenance: ResourceProvenance) -> bool:
|
|
13
|
+
for node in context.nodes(ast.Name, ast.Attribute):
|
|
14
|
+
qualified = context.imports.resolved_qualified_name(node)
|
|
15
|
+
if qualified in {"loguru", "loguru.logger"} and not provenance.imported(node, qualified):
|
|
16
|
+
return False
|
|
17
|
+
return True
|
|
@@ -26,6 +26,7 @@ _RESOURCE_OWNERS = MappingProxyType(
|
|
|
26
26
|
"httpx": frozenset({"Client", "AsyncClient"}),
|
|
27
27
|
"asyncio": frozenset({"sleep", "CancelledError", "exceptions.CancelledError"}),
|
|
28
28
|
"contextlib": frozenset({"ExitStack", "AsyncExitStack", "_BaseExitStack"}),
|
|
29
|
+
"functools": frozenset({"cache", "lru_cache", "cached_property"}),
|
|
29
30
|
"subprocess": frozenset({"Popen", "TimeoutExpired"}),
|
|
30
31
|
"re": frozenset({"compile", "Match", "Pattern"}),
|
|
31
32
|
"operator": frozenset({"setitem", "delitem", "iadd", "iconcat", "imul", "iand", "isub", "ixor", "ior"}),
|
|
@@ -309,7 +310,14 @@ class ResourceProvenance:
|
|
|
309
310
|
isinstance(parent, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef)
|
|
310
311
|
and node in parent.decorator_list
|
|
311
312
|
and self.context.imports.resolved_qualified_name(node)
|
|
312
|
-
in {
|
|
313
|
+
in {
|
|
314
|
+
"typing.final",
|
|
315
|
+
"typing_extensions.final",
|
|
316
|
+
"dataclasses.dataclass",
|
|
317
|
+
"functools.cache",
|
|
318
|
+
"functools.lru_cache",
|
|
319
|
+
"functools.cached_property",
|
|
320
|
+
}
|
|
313
321
|
)
|
|
314
322
|
)
|
|
315
323
|
|
|
@@ -50,7 +50,8 @@ class NoAsyncFunctionCache(Rule):
|
|
|
50
50
|
"Async generators, transformed functions, ambiguous or rebound imports, and generated files are excluded.",
|
|
51
51
|
"Wildcard imports and names rebound by relative imports or imports outside the module body are excluded conservatively.",
|
|
52
52
|
"lru_cache with disabled, dynamic, or unpacked maxsize arguments is excluded; an omitted size or literal positive integer or None is required.",
|
|
53
|
-
"
|
|
53
|
+
"Visible mutations, deletion, or escape of functools providers invalidate their native identity conservatively.",
|
|
54
|
+
"Indirect wrapping calls and opaque runtime or interprocedural mutations are not resolved. Test code is checked because coroutine reuse fails there too.",
|
|
54
55
|
"No automatic rewrite is offered because cache lifetime, cancellation, and concurrency require an explicit design decision.",
|
|
55
56
|
),
|
|
56
57
|
examples=(
|
|
@@ -99,6 +100,10 @@ class NoAsyncFunctionCache(Rule):
|
|
|
99
100
|
or is_suppressed(context.source_lines, decorator.lineno, self.code)
|
|
100
101
|
):
|
|
101
102
|
continue
|
|
103
|
+
target = decorator.func if isinstance(decorator, ast.Call) else decorator
|
|
104
|
+
qualified = context.imports.resolved_qualified_name(target)
|
|
105
|
+
if qualified is None or not context.static_resource_provenance.imported(target, qualified):
|
|
106
|
+
continue
|
|
102
107
|
diagnostics.append(
|
|
103
108
|
Diagnostic(
|
|
104
109
|
path=context.path,
|
|
@@ -18,10 +18,12 @@ from sarj_python_lint.rule_base import (
|
|
|
18
18
|
is_suppressed,
|
|
19
19
|
)
|
|
20
20
|
from sarj_python_lint.rules._imports import ambiguous_import_names as _ambiguous_import_names
|
|
21
|
+
from sarj_python_lint.rules._loguru import logger_imports_untouched
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from sarj_python_lint._file_context import PythonFileContext
|
|
26
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
_METHODS = frozenset({"trace", "debug", "info", "success", "warning", "error", "critical", "log"})
|
|
@@ -46,8 +48,8 @@ class NoLoguruExcInfo(Rule):
|
|
|
46
48
|
"Safe fixes handle only a direct proven Loguru logger call with literal arguments and keywords, replacing unused exc_info=True with opt(exception=True). Existing bind/opt chains, dynamic argument evaluation and commented calls remain review-only.",
|
|
47
49
|
"Only unambiguous Loguru imports and immediate bind/opt chains with literal exc_info=True are checked.",
|
|
48
50
|
"A literal message is required; templates using exc_info or the record, explicit exception capture, and exception() are excluded.",
|
|
49
|
-
"Unknown receivers, stored logger aliases, dynamic exc_info values, ambiguous imports, and generated files are excluded.",
|
|
50
|
-
"
|
|
51
|
+
"Unknown receivers, stored logger aliases, dynamic exc_info values, ambiguous or escaped imports, and generated files are excluded.",
|
|
52
|
+
"Visible namespace or logger mutations invalidate import proof. Interprocedural effects and runtime monkeypatches are not resolved.",
|
|
51
53
|
"An intentionally stored field can use a diagnostic-specific suppression. More complex logging context remains review-only.",
|
|
52
54
|
),
|
|
53
55
|
examples=(
|
|
@@ -121,18 +123,22 @@ class NoLoguruExcInfo(Rule):
|
|
|
121
123
|
shadowed = _ambiguous_import_names(context)
|
|
122
124
|
if "*" in shadowed:
|
|
123
125
|
return []
|
|
126
|
+
provenance: ResourceProvenance | None = None
|
|
124
127
|
diagnostics: list[Diagnostic] = []
|
|
125
128
|
for call in context.nodes(ast.Call):
|
|
126
129
|
if not isinstance(call.func, ast.Attribute) or call.func.attr not in _METHODS:
|
|
127
130
|
continue
|
|
128
|
-
keyword =
|
|
129
|
-
if keyword is None or
|
|
131
|
+
keyword = _unused_exc_info_keyword(call)
|
|
132
|
+
if keyword is None or is_suppressed(context.source_lines, keyword.lineno, self.code):
|
|
130
133
|
continue
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
receiver = _unconfigured_loguru_root(call.func.value, context, shadowed)
|
|
135
|
+
if receiver is None:
|
|
136
|
+
continue
|
|
137
|
+
if provenance is None:
|
|
138
|
+
provenance = context.static_resource_provenance
|
|
139
|
+
if not logger_imports_untouched(context, provenance):
|
|
140
|
+
return []
|
|
141
|
+
if not provenance.imported(receiver, "loguru.logger"):
|
|
136
142
|
continue
|
|
137
143
|
diagnostics.append(
|
|
138
144
|
Diagnostic(
|
|
@@ -147,12 +153,23 @@ class NoLoguruExcInfo(Rule):
|
|
|
147
153
|
return diagnostics
|
|
148
154
|
|
|
149
155
|
|
|
150
|
-
def
|
|
156
|
+
def _unused_exc_info_keyword(call: ast.Call) -> ast.keyword | None:
|
|
157
|
+
keyword = next((item for item in call.keywords if item.arg == "exc_info"), None)
|
|
158
|
+
if keyword is None or not isinstance(keyword.value, ast.Constant) or keyword.value.value is not True:
|
|
159
|
+
return None
|
|
160
|
+
if not _has_unused_exc_info(call):
|
|
161
|
+
return None
|
|
162
|
+
return keyword
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _unconfigured_loguru_root(
|
|
166
|
+
receiver: ast.expr, context: PythonFileContext, shadowed: frozenset[str]
|
|
167
|
+
) -> ast.expr | None:
|
|
151
168
|
opted = False
|
|
152
169
|
while isinstance(receiver, ast.Call) and isinstance(receiver.func, ast.Attribute):
|
|
153
170
|
method = receiver.func.attr
|
|
154
171
|
if method not in {"bind", "opt"}:
|
|
155
|
-
return
|
|
172
|
+
return None
|
|
156
173
|
if method == "opt" and not opted:
|
|
157
174
|
opted = True
|
|
158
175
|
if any(
|
|
@@ -163,14 +180,16 @@ def _is_unconfigured_loguru(receiver: ast.expr, context: PythonFileContext, shad
|
|
|
163
180
|
)
|
|
164
181
|
for keyword in receiver.keywords
|
|
165
182
|
):
|
|
166
|
-
return
|
|
183
|
+
return None
|
|
167
184
|
receiver = receiver.func.value
|
|
168
185
|
root = receiver
|
|
169
186
|
while isinstance(root, ast.Attribute):
|
|
170
187
|
root = root.value
|
|
171
|
-
|
|
188
|
+
if (isinstance(root, ast.Name) and root.id in shadowed) or not context.imports.resolves(
|
|
172
189
|
receiver, sources=_LOGURU, symbol="logger"
|
|
173
|
-
)
|
|
190
|
+
):
|
|
191
|
+
return None
|
|
192
|
+
return receiver
|
|
174
193
|
|
|
175
194
|
|
|
176
195
|
def _has_unused_exc_info(call: ast.Call) -> bool:
|
|
@@ -18,6 +18,7 @@ from sarj_python_lint.rule_base import (
|
|
|
18
18
|
Severity,
|
|
19
19
|
is_suppressed,
|
|
20
20
|
)
|
|
21
|
+
from sarj_python_lint.rules._loguru import logger_imports_untouched
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
@@ -121,7 +122,7 @@ class NoLoguruPrintfFormat(Rule):
|
|
|
121
122
|
continue
|
|
122
123
|
if provenance is None:
|
|
123
124
|
provenance = context.static_resource_provenance
|
|
124
|
-
if not
|
|
125
|
+
if not logger_imports_untouched(context, provenance):
|
|
125
126
|
return []
|
|
126
127
|
if not provenance.imported(receiver, "loguru.logger") or is_suppressed(
|
|
127
128
|
context.source_lines, call.lineno, self.code
|
|
@@ -140,14 +141,6 @@ class NoLoguruPrintfFormat(Rule):
|
|
|
140
141
|
return diagnostics
|
|
141
142
|
|
|
142
143
|
|
|
143
|
-
def _logger_imports_untouched(context: PythonFileContext, provenance: ResourceProvenance) -> bool:
|
|
144
|
-
for node in context.nodes(ast.Name, ast.Attribute):
|
|
145
|
-
qualified = context.imports.resolved_qualified_name(node)
|
|
146
|
-
if qualified in {"loguru", "loguru.logger"} and not provenance.imported(node, qualified):
|
|
147
|
-
return False
|
|
148
|
-
return True
|
|
149
|
-
|
|
150
|
-
|
|
151
144
|
def _logger_root(receiver: ast.expr) -> ast.expr | None:
|
|
152
145
|
while isinstance(receiver, ast.Call) and isinstance(receiver.func, ast.Attribute):
|
|
153
146
|
if (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
+
from dataclasses import dataclass
|
|
4
5
|
from pathlib import PurePosixPath
|
|
5
6
|
from typing import TYPE_CHECKING, ClassVar, override
|
|
6
7
|
|
|
@@ -15,6 +16,7 @@ from sarj_python_lint.rule_base import (
|
|
|
15
16
|
RuleExample,
|
|
16
17
|
)
|
|
17
18
|
from sarj_python_lint.rules._logging import LOG_METHODS, is_logger_expr
|
|
19
|
+
from sarj_python_lint.rules._loguru import logger_imports_untouched
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
if TYPE_CHECKING:
|
|
@@ -22,6 +24,7 @@ if TYPE_CHECKING:
|
|
|
22
24
|
from pathlib import Path
|
|
23
25
|
|
|
24
26
|
from sarj_python_lint._file_context import PythonFileContext
|
|
27
|
+
from sarj_python_lint.rules._resource_provenance import ResourceProvenance
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
# A whole redaction token (`token_prefix`, `password_hash`, `secret_masked`,
|
|
@@ -30,6 +33,16 @@ if TYPE_CHECKING:
|
|
|
30
33
|
_REDACTION_TOKENS = frozenset(
|
|
31
34
|
{"prefix", "suffix", "redact", "redacted", "mask", "masked", "hash", "hint", "len", "length", "tag"}
|
|
32
35
|
)
|
|
36
|
+
_LOGURU_OPT_PARAMETERS = frozenset({"exception", "record", "lazy", "colors", "raw", "capture", "depth", "ansi"})
|
|
37
|
+
_LOGURU_LOG_METHODS = frozenset(
|
|
38
|
+
{"trace", "debug", "info", "success", "warning", "error", "critical", "exception", "log"}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class _OptBindingCandidate:
|
|
44
|
+
root: ast.expr
|
|
45
|
+
values: tuple[ast.expr, ...]
|
|
33
46
|
|
|
34
47
|
|
|
35
48
|
def _is_raw_secret_reference(value: ast.expr) -> bool:
|
|
@@ -89,6 +102,9 @@ class NoSecretInLog(Rule):
|
|
|
89
102
|
category=RuleCategory.SECURITY,
|
|
90
103
|
limitations=(
|
|
91
104
|
"Detection covers direct secret-named message and argument values, terminal attributes, constant secret-key subscripts, identity slices, f-string and literal format arguments, string concatenation, nested literal containers, literal keyword unpacking, and immediate structured-logger bindings.",
|
|
105
|
+
"Immediate opt calls on a proven untouched native Loguru logger expose only the first bind's values. Positional or expanded chain arguments, unsupported opt keyword names, custom wrappers, stored aliases, and visibly mutated or escaped imports are excluded from this added coverage.",
|
|
106
|
+
"This added coverage requires a native Loguru logging method and a positional message, with no terminal keyword or expanded arguments. Terminal fields are excluded even with capture=False, rather than evaluating option state or merging fields that may override bindings.",
|
|
107
|
+
"Earlier inner bindings are not merged: a safe outer binding hides earlier bindings, including nonconflicting fields. Opaque runtime effects remain unresolved.",
|
|
92
108
|
"Logger recognition and secret classification are lexical; general aliases, arbitrary calls, dynamic containers, and interprocedural dataflow are not inspected.",
|
|
93
109
|
),
|
|
94
110
|
examples=(
|
|
@@ -125,28 +141,24 @@ class NoSecretInLog(Rule):
|
|
|
125
141
|
tree = context.tree
|
|
126
142
|
if tree is None:
|
|
127
143
|
return []
|
|
144
|
+
provenance: ResourceProvenance | None = None
|
|
145
|
+
untouched = False
|
|
128
146
|
diags: list[Diagnostic] = []
|
|
129
147
|
for node in context.nodes(ast.Call):
|
|
130
148
|
function = node.func
|
|
131
149
|
if not isinstance(function, ast.Attribute) or not _is_logging_call(node):
|
|
132
150
|
continue
|
|
133
|
-
|
|
134
|
-
for arg in node.args[message_index:]:
|
|
135
|
-
diags.extend(
|
|
136
|
-
Diagnostic(
|
|
137
|
-
path=path,
|
|
138
|
-
line=unsafe.lineno,
|
|
139
|
-
col=unsafe.col_offset + 1,
|
|
140
|
-
code=self.code,
|
|
141
|
-
message="Credential-like reference is interpolated into a log message and may expose sensitive data — omit it.",
|
|
142
|
-
)
|
|
143
|
-
for unsafe in _unsafe_message_values(arg)
|
|
144
|
-
)
|
|
151
|
+
_collect_message_secrets(path, node, self.code, diags)
|
|
145
152
|
_collect_keyword_secrets(path, node, self.code, diags)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
bound_values = _bound_logger_values(function.value)
|
|
154
|
+
candidate = _opt_binding_candidate(node)
|
|
155
|
+
if candidate is not None:
|
|
156
|
+
if provenance is None:
|
|
157
|
+
provenance = context.static_resource_provenance
|
|
158
|
+
untouched = logger_imports_untouched(context, provenance)
|
|
159
|
+
if untouched and provenance.imported(candidate.root, "loguru.logger"):
|
|
160
|
+
bound_values = candidate.values
|
|
161
|
+
diags.extend(_diagnostic(path, value, self.code, "structured logger binding") for value in bound_values)
|
|
150
162
|
return diags
|
|
151
163
|
|
|
152
164
|
|
|
@@ -158,6 +170,50 @@ def _bound_logger_values(receiver: ast.expr) -> tuple[ast.expr, ...]:
|
|
|
158
170
|
return tuple(secret for keyword in receiver.keywords for secret in _unsafe_message_values(keyword.value))
|
|
159
171
|
|
|
160
172
|
|
|
173
|
+
def _opt_binding_candidate(call: ast.Call) -> _OptBindingCandidate | None:
|
|
174
|
+
receiver = _loguru_terminal_receiver(call)
|
|
175
|
+
if receiver is None:
|
|
176
|
+
return None
|
|
177
|
+
binding = receiver
|
|
178
|
+
if not isinstance(binding, ast.Call) or not isinstance(binding.func, ast.Attribute) or binding.func.attr != "opt":
|
|
179
|
+
return None
|
|
180
|
+
while isinstance(binding, ast.Call) and isinstance(binding.func, ast.Attribute) and binding.func.attr == "opt":
|
|
181
|
+
binding = binding.func.value
|
|
182
|
+
if not isinstance(binding, ast.Call) or not isinstance(binding.func, ast.Attribute) or binding.func.attr != "bind":
|
|
183
|
+
return None
|
|
184
|
+
values = _bound_logger_values(binding)
|
|
185
|
+
if not values:
|
|
186
|
+
return None
|
|
187
|
+
root = _loguru_chain_root(receiver)
|
|
188
|
+
if root is None:
|
|
189
|
+
return None
|
|
190
|
+
return _OptBindingCandidate(root=root, values=values)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _loguru_terminal_receiver(call: ast.Call) -> ast.expr | None:
|
|
194
|
+
function = call.func
|
|
195
|
+
if (
|
|
196
|
+
not isinstance(function, ast.Attribute)
|
|
197
|
+
or function.attr not in _LOGURU_LOG_METHODS
|
|
198
|
+
or len(call.args) <= int(function.attr == "log")
|
|
199
|
+
or call.keywords
|
|
200
|
+
or any(isinstance(argument, ast.Starred) for argument in call.args)
|
|
201
|
+
):
|
|
202
|
+
return None
|
|
203
|
+
return function.value
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _loguru_chain_root(receiver: ast.expr) -> ast.expr | None:
|
|
207
|
+
root = receiver
|
|
208
|
+
while isinstance(root, ast.Call) and isinstance(root.func, ast.Attribute):
|
|
209
|
+
if root.func.attr not in {"bind", "opt"} or root.args or any(keyword.arg is None for keyword in root.keywords):
|
|
210
|
+
return None
|
|
211
|
+
if root.func.attr == "opt" and any(keyword.arg not in _LOGURU_OPT_PARAMETERS for keyword in root.keywords):
|
|
212
|
+
return None
|
|
213
|
+
root = root.func.value
|
|
214
|
+
return root
|
|
215
|
+
|
|
216
|
+
|
|
161
217
|
def _diagnostic(path: Path, value: ast.expr, code: str, context: str) -> Diagnostic:
|
|
162
218
|
return Diagnostic(
|
|
163
219
|
path=path,
|
|
@@ -183,6 +239,24 @@ def _unsafe_formatted_values(parts: list[ast.expr]) -> tuple[ast.expr, ...]:
|
|
|
183
239
|
)
|
|
184
240
|
|
|
185
241
|
|
|
242
|
+
def _collect_message_secrets(path: Path, node: ast.Call, code: str, diags: list[Diagnostic]) -> None:
|
|
243
|
+
function = node.func
|
|
244
|
+
if not isinstance(function, ast.Attribute):
|
|
245
|
+
return
|
|
246
|
+
message_index = 1 if function.attr == "log" else 0
|
|
247
|
+
for arg in node.args[message_index:]:
|
|
248
|
+
diags.extend(
|
|
249
|
+
Diagnostic(
|
|
250
|
+
path=path,
|
|
251
|
+
line=unsafe.lineno,
|
|
252
|
+
col=unsafe.col_offset + 1,
|
|
253
|
+
code=code,
|
|
254
|
+
message="Credential-like reference is interpolated into a log message and may expose sensitive data — omit it.",
|
|
255
|
+
)
|
|
256
|
+
for unsafe in _unsafe_message_values(arg)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
186
260
|
def _collect_keyword_secrets(path: Path, node: ast.Call, code: str, diags: list[Diagnostic]) -> None:
|
|
187
261
|
for kw in node.keywords:
|
|
188
262
|
if kw.arg is None:
|
|
@@ -22,6 +22,7 @@ from sarj_python_lint.rule_base import (
|
|
|
22
22
|
)
|
|
23
23
|
from sarj_python_lint.rules._annotation_semantics import bit_or_members as _flatten_union
|
|
24
24
|
from sarj_python_lint.rules._ast_index import nodes, parent_map, walk as walk_ast
|
|
25
|
+
from sarj_python_lint.rules._imports import ABC_SOURCES
|
|
25
26
|
from sarj_python_lint.rules._inherited_contract import inherited_contract
|
|
26
27
|
from sarj_python_lint.rules._nominal_project import NominalSource
|
|
27
28
|
|
|
@@ -160,9 +161,48 @@ class PreferNominalIdTypes(Rule):
|
|
|
160
161
|
"Non-ID role discovery uses exact snake-case names and proven matching carriers; it does not infer brands from names alone. Dependency discovery respects declared workspace members and exclusions, and excludes optional and transitive dependencies.",
|
|
161
162
|
"Rebound aliases, generic aliases, carrier names shadowed by type parameters, and mutated or escaped canonical provider namespaces are skipped conservatively across the file.",
|
|
162
163
|
"Generated code, migrations, external adapters, operational context, raw schemas, SQLAlchemy Mapped fields, ambiguous imports, and unlike carrier shapes are excluded.",
|
|
163
|
-
"
|
|
164
|
+
"Canonical object, ABC, Protocol and Generic bases do not hide fields or methods declared by the class. Mutated, escaped or rebound providers remain unknown. Other explicit bases stay excluded without requiring an override decorator; their inherited API may prescribe raw carriers. Local custom bases do not prove runtime member ownership remains closed.",
|
|
164
165
|
),
|
|
165
166
|
examples=(
|
|
167
|
+
RuleExample(
|
|
168
|
+
example_id="owned-abstract-contract-id-roles",
|
|
169
|
+
scenario="owned-contract",
|
|
170
|
+
title="Abstract contracts declare their own nominal ID boundary",
|
|
171
|
+
outcome=ExampleOutcome.MATCH,
|
|
172
|
+
files=(
|
|
173
|
+
ExampleFile.python(
|
|
174
|
+
"app/contracts.py",
|
|
175
|
+
"from abc import ABC, abstractmethod\n"
|
|
176
|
+
"class FilePort(ABC):\n"
|
|
177
|
+
" @abstractmethod\n"
|
|
178
|
+
" def move(self, file_id: str, folder_id: str) -> None: ...\n",
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
focus_path=PurePosixPath("app/contracts.py"),
|
|
182
|
+
expected_count=1,
|
|
183
|
+
public=True,
|
|
184
|
+
),
|
|
185
|
+
RuleExample(
|
|
186
|
+
example_id="owned-abstract-contract-nominal-ids",
|
|
187
|
+
scenario="owned-contract",
|
|
188
|
+
title="Abstract contracts use distinct domain identifiers",
|
|
189
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
190
|
+
files=(
|
|
191
|
+
ExampleFile.python(
|
|
192
|
+
"app/contracts.py",
|
|
193
|
+
"from abc import ABC, abstractmethod\n"
|
|
194
|
+
"from typing import NewType\n"
|
|
195
|
+
"FileId = NewType('FileId', str)\n"
|
|
196
|
+
"FolderId = NewType('FolderId', str)\n"
|
|
197
|
+
"class FilePort(ABC):\n"
|
|
198
|
+
" @abstractmethod\n"
|
|
199
|
+
" def move(self, file_id: FileId, folder_id: FolderId) -> None: ...\n",
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
focus_path=PurePosixPath("app/contracts.py"),
|
|
203
|
+
expected_count=0,
|
|
204
|
+
public=True,
|
|
205
|
+
),
|
|
166
206
|
RuleExample(
|
|
167
207
|
example_id="swappable-primitive-id-roles",
|
|
168
208
|
title="Two domain ID roles share the same primitive carrier",
|
|
@@ -289,7 +329,7 @@ class PreferNominalIdTypes(Rule):
|
|
|
289
329
|
|
|
290
330
|
imports = context.imports
|
|
291
331
|
original_facts = _type_facts(tree, imports, node_index=context.node_index)
|
|
292
|
-
boundaries = _boundary_nodes(tree, imports)
|
|
332
|
+
boundaries = _boundary_nodes(tree, imports, original_facts)
|
|
293
333
|
facts = (
|
|
294
334
|
self._expanded_facts(context, tree, imports)
|
|
295
335
|
if any(_can_expand(node, imports, original_facts) for node in boundaries)
|
|
@@ -388,6 +428,7 @@ def _is_excluded_path(path: Path) -> bool:
|
|
|
388
428
|
def _boundary_nodes(
|
|
389
429
|
tree: ast.Module,
|
|
390
430
|
imports: ImportIndex,
|
|
431
|
+
facts: _TypeFacts,
|
|
391
432
|
) -> list[ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef]:
|
|
392
433
|
result: list[ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef] = []
|
|
393
434
|
for statement in tree.body:
|
|
@@ -397,7 +438,7 @@ def _boundary_nodes(
|
|
|
397
438
|
continue
|
|
398
439
|
if not isinstance(statement, ast.ClassDef):
|
|
399
440
|
continue
|
|
400
|
-
if inherited_contract(statement):
|
|
441
|
+
if inherited_contract(statement) and not _has_only_marker_bases(statement, imports, facts.shadowed_names):
|
|
401
442
|
continue
|
|
402
443
|
if _name_parts(statement.name) & _OPERATIONAL_NAME_PARTS:
|
|
403
444
|
continue
|
|
@@ -408,6 +449,23 @@ def _boundary_nodes(
|
|
|
408
449
|
return result
|
|
409
450
|
|
|
410
451
|
|
|
452
|
+
def _has_only_marker_bases(node: ast.ClassDef, imports: ImportIndex, shadowed_names: frozenset[str]) -> bool:
|
|
453
|
+
for expression in node.bases:
|
|
454
|
+
base = expression.value if isinstance(expression, ast.Subscript) else expression
|
|
455
|
+
if _expression_is_shadowed(base, shadowed_names):
|
|
456
|
+
return False
|
|
457
|
+
if isinstance(base, ast.Name) and base.id == "object" and imports.builtin_is_unshadowed("object"):
|
|
458
|
+
continue
|
|
459
|
+
if imports.resolves(base, sources=_BUILTIN_SOURCES, symbol="object"):
|
|
460
|
+
continue
|
|
461
|
+
if imports.resolves(base, sources=ABC_SOURCES, symbol="ABC"):
|
|
462
|
+
continue
|
|
463
|
+
if imports.resolved_symbol(base, sources=_TYPING_SOURCES) in {"Generic", "Protocol"}:
|
|
464
|
+
continue
|
|
465
|
+
return False
|
|
466
|
+
return True
|
|
467
|
+
|
|
468
|
+
|
|
411
469
|
def _is_operational_function(node: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
|
|
412
470
|
return bool(_name_parts(node.name) & _OPERATIONAL_NAME_PARTS)
|
|
413
471
|
|
|
@@ -725,7 +783,7 @@ def _is_builtin_namespace_call(node: ast.Call, imports: ImportIndex) -> bool:
|
|
|
725
783
|
|
|
726
784
|
|
|
727
785
|
def _escaped_provider_names(tree: ast.Module, imports: ImportIndex, *, node_index: NodeIndex | None) -> set[str]:
|
|
728
|
-
providers = _TYPING_SOURCES | _BUILTIN_SOURCES | _UUID_SOURCES
|
|
786
|
+
providers = _TYPING_SOURCES | _BUILTIN_SOURCES | _UUID_SOURCES | ABC_SOURCES
|
|
729
787
|
namespaces = {
|
|
730
788
|
name: binding.module
|
|
731
789
|
for name, binding in imports.bindings.items()
|
|
@@ -763,7 +821,7 @@ def _mutated_builtin_names(node: ast.expr, member: str | None, imports: ImportIn
|
|
|
763
821
|
binding = imports.bindings.get(node.id)
|
|
764
822
|
if binding is None or binding.module != "builtins" or binding.symbol is not None:
|
|
765
823
|
return set()
|
|
766
|
-
carriers = {"str", "int"} | _BUILTIN_COLLECTIONS
|
|
824
|
+
carriers = {"str", "int", "object"} | _BUILTIN_COLLECTIONS
|
|
767
825
|
changed = carriers if member is None or member.startswith("__") else carriers & {member}
|
|
768
826
|
if not changed:
|
|
769
827
|
return set()
|
|
@@ -313,6 +313,8 @@ class _Analysis:
|
|
|
313
313
|
state.constants[target.id] = value
|
|
314
314
|
|
|
315
315
|
def executions(self, statement: ast.stmt, state: _State) -> None:
|
|
316
|
+
if not state.bindings:
|
|
317
|
+
return
|
|
316
318
|
for node in _eager_nodes(statement):
|
|
317
319
|
if (
|
|
318
320
|
not isinstance(node, ast.Call)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_analysis_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_aliases.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_annotations.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_comments.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_docs.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_enums.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_inert.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_parameters.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_repeated.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_security.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_autofix_tests.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_file_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_prepared_sources.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_python_target.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_rule_descriptors.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/_secret_names.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/application_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/interpreter_argv.py
RENAMED
|
File without changes
|
{sarj_python_lint-1.108.1 → sarj_python_lint-1.108.51}/src/sarj_python_lint/json_boundary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|