sarj-python-lint 0.29.0__tar.gz → 0.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/PKG-INFO +1 -1
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/pyproject.toml +1 -1
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_implicit_attribute_access.py +52 -1
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/.gitignore +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/README.md +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/conditional_assertion_in_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/duplicate_test_body.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/inefficient_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/interaction_only_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/kwarg_heavy_construction_in_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/kwonly_same_type_params.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_aggregation_in_store_query.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_noqa.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_file_level_suppression.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_fstring_in_log.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_gen_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_isinstance_union_chain.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_optional_tenant_predicate.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_patching_system_under_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_query_with_many_joins.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_raw_sql_in_tests.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_sentinel_return_on_except.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_sequential_await.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_sleep_in_test_body.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_stdlib_logging.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_unreachable_after_terminal.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_match_pattern_destructuring.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_real_store_in_tests.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_timedelta_for_durations.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/single_public_export.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/sleep_with_computed_arg_in_test.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/tautological_mock_assertion.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/test_loops_over_literal_cases.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/trivially_true_assertion.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/xfail_requires_strict.py +0 -0
- {sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/zero_assertion_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://github.com/sarj-ai/standards/tree/main/packages/python
|
|
6
6
|
Project-URL: Repository, https://github.com/sarj-ai/standards
|
|
@@ -44,6 +44,34 @@ _EXCLUDED_BASES = {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
# Typing constructs subscripted with string literals. `Literal["x"]` is a type,
|
|
48
|
+
# not a lookup, so the rule's advice ("parse declaratively with Pydantic") is
|
|
49
|
+
# nonsensical there -- the annotation already IS the declarative schema.
|
|
50
|
+
_TYPE_SUBSCRIPTS = frozenset(
|
|
51
|
+
{
|
|
52
|
+
"Literal",
|
|
53
|
+
"Annotated",
|
|
54
|
+
"TypedDict",
|
|
55
|
+
"NamedTuple",
|
|
56
|
+
"Field",
|
|
57
|
+
"Doc",
|
|
58
|
+
"Required",
|
|
59
|
+
"NotRequired",
|
|
60
|
+
"ReadOnly",
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _looks_like_route_or_url(value: str) -> bool:
|
|
66
|
+
"""Report whether a `.get()` argument is a route path or URL rather than a key.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
True for `"/users/{id}"`-shaped paths and anything carrying a scheme.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
return value.startswith("/") or "://" in value
|
|
73
|
+
|
|
74
|
+
|
|
47
75
|
def _get_base_name(node: ast.expr) -> str | None:
|
|
48
76
|
if isinstance(node, ast.Name):
|
|
49
77
|
return node.id
|
|
@@ -100,6 +128,14 @@ def _get_key(node: ast.Call) -> str | None:
|
|
|
100
128
|
first = node.args[0]
|
|
101
129
|
if not isinstance(first, ast.Constant) or not isinstance(first.value, str):
|
|
102
130
|
return None
|
|
131
|
+
# `.get()` is also the HTTP verb and the route-registration decorator, and
|
|
132
|
+
# both take a string first argument, so the method name alone cannot tell
|
|
133
|
+
# them from a mapping lookup. The ARGUMENT can: a URL or a route path is not
|
|
134
|
+
# a dictionary key. Measured on bulbul + noura-be, this shape was 168 of the
|
|
135
|
+
# rule's 1,756 findings (9.6%) -- `@router.get("/available-events")` and
|
|
136
|
+
# `await self.http_client.get(url)` were reported as implicit schema access.
|
|
137
|
+
if _looks_like_route_or_url(first.value):
|
|
138
|
+
return None
|
|
103
139
|
return None if _get_base_name(func.value) in _EXCLUDED_BASES else first.value
|
|
104
140
|
|
|
105
141
|
|
|
@@ -111,10 +147,25 @@ def _subscript_key(node: ast.Subscript) -> str | None:
|
|
|
111
147
|
the base is one of the excluded receivers.
|
|
112
148
|
|
|
113
149
|
"""
|
|
150
|
+
# Writing to a mapping is the opposite of the defect. This rule is about
|
|
151
|
+
# PLUCKING fields out of a payload whose schema is already known -- building
|
|
152
|
+
# a dict up key by key (`field_dict["x"] = x`, `params["status"] = ...`) is
|
|
153
|
+
# ordinary construction, and a Pydantic model does not replace it. Measured
|
|
154
|
+
# on bulbul + noura-be this was 503 of 1,756 findings (28.6%), the single
|
|
155
|
+
# largest source, and every sampled instance was an assignment target.
|
|
156
|
+
if isinstance(node.ctx, (ast.Store, ast.Del)):
|
|
157
|
+
return None
|
|
158
|
+
# `Literal["a"]`, `Annotated[T, "..."]` and friends are type expressions that
|
|
159
|
+
# merely LOOK like subscripts. They are not dictionary access at all, and no
|
|
160
|
+
# Pydantic model can replace them -- `Literal["user"]` IS the schema. 470 of
|
|
161
|
+
# 1,756 findings (26.8%) were this, second only to assignment targets.
|
|
162
|
+
base_name = _get_base_name(node.value)
|
|
163
|
+
if base_name in _TYPE_SUBSCRIPTS:
|
|
164
|
+
return None
|
|
114
165
|
index = node.slice
|
|
115
166
|
if not isinstance(index, ast.Constant) or not isinstance(index.value, str):
|
|
116
167
|
return None
|
|
117
|
-
return None if
|
|
168
|
+
return None if base_name in _EXCLUDED_BASES else index.value
|
|
118
169
|
|
|
119
170
|
|
|
120
171
|
def _is_test_path(path: Path) -> bool:
|
|
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-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/_first_party.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
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_fstring_in_log.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-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_select_star.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/no_stdlib_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.29.0 → sarj_python_lint-0.30.0}/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
|
|
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-0.29.0 → sarj_python_lint-0.30.0}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|