sarj-python-lint 0.40.0__tar.gz → 0.41.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.40.0 → sarj_python_lint-0.41.0}/PKG-INFO +1 -1
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/pyproject.toml +1 -1
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_comments.py +164 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_comment_cruft.py +17 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_restated_comment.py +7 -1
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/.gitignore +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/README.md +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/conditional_assertion_in_test.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/duplicate_test_body.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/duplicated_override_docstring.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/inefficient_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/interaction_only_test.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/kwarg_heavy_construction_in_test.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/kwonly_same_type_params.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_aggregation_in_store_query.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_noqa.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_file_level_suppression.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_fstring_in_log.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_gen_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_isinstance_union_chain.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_optional_tenant_predicate.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_query_with_many_joins.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_raw_sql_in_tests.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_sentinel_return_on_except.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_sequential_await.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_sleep_in_test_body.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_stdlib_logging.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_unreachable_after_terminal.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_match_pattern_destructuring.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_real_store_in_tests.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_timedelta_for_durations.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/single_public_export.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/sleep_with_computed_arg_in_test.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/tautological_mock_assertion.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/test_loops_over_literal_cases.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/trivially_true_assertion.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/xfail_requires_strict.py +0 -0
- {sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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.41.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
|
|
@@ -26,14 +26,18 @@ tokenize each file once between them rather than once each.
|
|
|
26
26
|
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
|
+
import ast
|
|
29
30
|
import io
|
|
30
31
|
import re
|
|
31
32
|
import tokenize
|
|
32
33
|
from typing import TYPE_CHECKING
|
|
33
34
|
|
|
35
|
+
from sarj_python_lint.rule_base import parse_or_none
|
|
36
|
+
|
|
34
37
|
|
|
35
38
|
if TYPE_CHECKING:
|
|
36
39
|
from collections.abc import Iterable, Sequence
|
|
40
|
+
from pathlib import Path
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
# S1 — external reference: URL, issue/ticket key, RFC/PEP/CVE, bare GitHub issue
|
|
@@ -476,3 +480,163 @@ def comment_runs(standalone: Sequence[tuple[int, int, str]]) -> list[list[tuple[
|
|
|
476
480
|
else:
|
|
477
481
|
runs.append([entry])
|
|
478
482
|
return runs
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# A comment wall is judged as a block, not as an isolated sentence. AI-written
|
|
486
|
+
# walkthroughs commonly evade the short-comment restatement rule by adding one
|
|
487
|
+
# or two filler words to every step; repetition is the evidence in that shape.
|
|
488
|
+
_WALL_MIN_STATEMENTS = 4
|
|
489
|
+
_WALL_MIN_COMMENTS = 3
|
|
490
|
+
_WALL_MIN_COMMENTED_RATIO = 0.6
|
|
491
|
+
_WALL_MIN_WEAK_RATIO = 0.75
|
|
492
|
+
_WALL_MAX_WORDS = 18
|
|
493
|
+
_WALL_MIN_CONTENT_WORDS = 1
|
|
494
|
+
_WALL_MIN_MATCHED_RATIO = 0.5
|
|
495
|
+
_WALL_MAX_NOVEL_WORDS = 2
|
|
496
|
+
_WALL_DIRECTIVE_RE = re.compile(
|
|
497
|
+
r"^(?:todo|fixme|hack\b|xxx|note:|nb:|warning:|important:|noqa|sarj-noqa|"
|
|
498
|
+
r"type:|pragma|pyright|mypy|fmt:|isort|ruff|pylint|flake8|nosec|nosemgrep|"
|
|
499
|
+
r"-\*-|!/|coding[:=])",
|
|
500
|
+
re.IGNORECASE,
|
|
501
|
+
)
|
|
502
|
+
_WALL_NARRATION_RE = re.compile(
|
|
503
|
+
r"^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|now|finally|lastly|"
|
|
504
|
+
r"add|append|assign|await|build|calculate|call|check|clear|close|compute|convert|copy|"
|
|
505
|
+
r"count|create|declare|define|delete|extract|fetch|filter|find|format|generate|get|"
|
|
506
|
+
r"handle|initialize|insert|iterate|join|load|log|loop|map|merge|open|parse|print|"
|
|
507
|
+
r"process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|"
|
|
508
|
+
r"start|stop|store|update|validate|wrap|write|apply|assemble|coerce|compress|"
|
|
509
|
+
r"disable|enable|lint|populate|prepare|redirect|register|resolve|run|stash|use)\b",
|
|
510
|
+
re.IGNORECASE,
|
|
511
|
+
)
|
|
512
|
+
_WALL_STATEMENTS = (
|
|
513
|
+
ast.Assign,
|
|
514
|
+
ast.AnnAssign,
|
|
515
|
+
ast.AugAssign,
|
|
516
|
+
ast.Assert,
|
|
517
|
+
ast.Delete,
|
|
518
|
+
ast.Expr,
|
|
519
|
+
ast.Raise,
|
|
520
|
+
ast.Return,
|
|
521
|
+
ast.If,
|
|
522
|
+
ast.For,
|
|
523
|
+
ast.AsyncFor,
|
|
524
|
+
ast.While,
|
|
525
|
+
ast.With,
|
|
526
|
+
ast.AsyncWith,
|
|
527
|
+
ast.Match,
|
|
528
|
+
ast.Try,
|
|
529
|
+
ast.TryStar,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _is_wall_statement(node: ast.stmt) -> bool:
|
|
534
|
+
"""Exclude docstrings while retaining executable expression statements."""
|
|
535
|
+
return isinstance(node, _WALL_STATEMENTS) and not (
|
|
536
|
+
isinstance(node, ast.Expr)
|
|
537
|
+
and isinstance(node.value, ast.Constant)
|
|
538
|
+
and isinstance(node.value.value, str)
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _weak_walkthrough_comment(body: str, statement: str) -> bool:
|
|
543
|
+
"""Whether a comment is weak evidence inside a repeated walkthrough."""
|
|
544
|
+
if (
|
|
545
|
+
not body
|
|
546
|
+
or body.endswith("?")
|
|
547
|
+
or len(body.split()) > _WALL_MAX_WORDS
|
|
548
|
+
or _WALL_DIRECTIVE_RE.match(body)
|
|
549
|
+
or is_protected(body)
|
|
550
|
+
):
|
|
551
|
+
return False
|
|
552
|
+
words = content_tokens(body)
|
|
553
|
+
if len(words) < _WALL_MIN_CONTENT_WORDS or not _WALL_NARRATION_RE.match(body):
|
|
554
|
+
return False
|
|
555
|
+
# The opener describes the operation and need not literally occur in the
|
|
556
|
+
# statement (`Fetch users` / `users = store.list()`). The remainder must be
|
|
557
|
+
# mostly corroborated; at most two filler words may be novel.
|
|
558
|
+
known = code_tokens(statement)
|
|
559
|
+
described = words[1:]
|
|
560
|
+
if not described:
|
|
561
|
+
return restates(words, known)
|
|
562
|
+
matched = sum(1 for word in described if restates([word], known))
|
|
563
|
+
novel = len(described) - matched
|
|
564
|
+
return matched / len(described) >= _WALL_MIN_MATCHED_RATIO and novel <= _WALL_MAX_NOVEL_WORDS
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _owned_statement_lists(owner: ast.AST) -> tuple[list[ast.stmt], ...]:
|
|
568
|
+
"""Return the distinct statement blocks directly owned by an AST node."""
|
|
569
|
+
if isinstance(
|
|
570
|
+
owner,
|
|
571
|
+
(
|
|
572
|
+
ast.Module,
|
|
573
|
+
ast.FunctionDef,
|
|
574
|
+
ast.AsyncFunctionDef,
|
|
575
|
+
ast.ClassDef,
|
|
576
|
+
ast.With,
|
|
577
|
+
ast.AsyncWith,
|
|
578
|
+
ast.ExceptHandler,
|
|
579
|
+
),
|
|
580
|
+
):
|
|
581
|
+
return (owner.body,)
|
|
582
|
+
if isinstance(owner, (ast.If, ast.For, ast.AsyncFor, ast.While)):
|
|
583
|
+
return owner.body, owner.orelse
|
|
584
|
+
if isinstance(owner, (ast.Try, ast.TryStar)):
|
|
585
|
+
return owner.body, owner.orelse, owner.finalbody
|
|
586
|
+
if isinstance(owner, ast.match_case):
|
|
587
|
+
return (owner.body,)
|
|
588
|
+
return ()
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def statement_comment_walls(
|
|
592
|
+
path: Path,
|
|
593
|
+
source: str,
|
|
594
|
+
standalone: Sequence[tuple[int, int, str]],
|
|
595
|
+
) -> dict[int, frozenset[int]]:
|
|
596
|
+
"""Return `{leader: member lines}` for repeated statement narration walls.
|
|
597
|
+
|
|
598
|
+
Each wall lives within one AST statement list (module, function, branch,
|
|
599
|
+
loop, handler). A comment must be directly above and aligned with the simple
|
|
600
|
+
statement it describes. Three weak comments are not enough by themselves:
|
|
601
|
+
comments must cover most of a block, and most attached comments must be
|
|
602
|
+
weak. This keeps an occasional label or a carefully documented exceptional
|
|
603
|
+
step from turning a whole function into a finding.
|
|
604
|
+
|
|
605
|
+
"""
|
|
606
|
+
tree = parse_or_none(path, source)
|
|
607
|
+
if tree is None:
|
|
608
|
+
return {}
|
|
609
|
+
comments = {line: (col, body) for line, col, body in standalone}
|
|
610
|
+
walls: dict[int, frozenset[int]] = {}
|
|
611
|
+
for owner in ast.walk(tree):
|
|
612
|
+
for value in _owned_statement_lists(owner):
|
|
613
|
+
statements = [item for item in value if _is_wall_statement(item)]
|
|
614
|
+
if len(statements) < _WALL_MIN_STATEMENTS:
|
|
615
|
+
continue
|
|
616
|
+
attached: list[tuple[int, int, bool]] = []
|
|
617
|
+
for index, statement in enumerate(statements):
|
|
618
|
+
entry = comments.get(statement.lineno - 1)
|
|
619
|
+
if entry is None or entry[0] != statement.col_offset:
|
|
620
|
+
continue
|
|
621
|
+
line = statement.lineno - 1
|
|
622
|
+
segment = ast.get_source_segment(source, statement) or ""
|
|
623
|
+
attached.append((index, line, _weak_walkthrough_comment(entry[1], segment)))
|
|
624
|
+
clusters: list[list[tuple[int, int, bool]]] = []
|
|
625
|
+
for item in attached:
|
|
626
|
+
if clusters and item[0] <= clusters[-1][-1][0] + 2:
|
|
627
|
+
clusters[-1].append(item)
|
|
628
|
+
else:
|
|
629
|
+
clusters.append([item])
|
|
630
|
+
for cluster in clusters:
|
|
631
|
+
span = cluster[-1][0] - cluster[0][0] + 1
|
|
632
|
+
weak = [line for _index, line, is_weak in cluster if is_weak]
|
|
633
|
+
if (
|
|
634
|
+
span < _WALL_MIN_STATEMENTS
|
|
635
|
+
or len(weak) < _WALL_MIN_COMMENTS
|
|
636
|
+
or len(cluster) / span < _WALL_MIN_COMMENTED_RATIO
|
|
637
|
+
or len(weak) / len(cluster) < _WALL_MIN_WEAK_RATIO
|
|
638
|
+
):
|
|
639
|
+
continue
|
|
640
|
+
leader = min(weak)
|
|
641
|
+
walls[leader] = frozenset(weak)
|
|
642
|
+
return walls
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
@@ -17,6 +17,7 @@ from sarj_python_lint.rules._comments import (
|
|
|
17
17
|
has_external_reference,
|
|
18
18
|
nested_comment_lines,
|
|
19
19
|
standalone_comments,
|
|
20
|
+
statement_comment_walls,
|
|
20
21
|
)
|
|
21
22
|
from sarj_python_lint.rules._paths import is_generated
|
|
22
23
|
|
|
@@ -503,7 +504,23 @@ class NoCommentCruft(Rule):
|
|
|
503
504
|
nested = nested_comment_lines(source)
|
|
504
505
|
enumerated = [line for line, _, body in standalone if _ENUMERATION_RE.match(body)]
|
|
505
506
|
license_header = _license_header_lines(standalone)
|
|
507
|
+
walls = statement_comment_walls(path, source, standalone)
|
|
508
|
+
wall_members = frozenset(line for members in walls.values() for line in members)
|
|
506
509
|
for line, col, body in standalone:
|
|
510
|
+
if line in walls:
|
|
511
|
+
diags[line] = Diagnostic(
|
|
512
|
+
path=path,
|
|
513
|
+
line=line,
|
|
514
|
+
col=col + 1,
|
|
515
|
+
code=self.code,
|
|
516
|
+
message=(
|
|
517
|
+
f"Statement comment wall ({len(walls[line])} narrated steps) — "
|
|
518
|
+
"delete the walkthrough and name the operations in code; keep only constraints or rationale."
|
|
519
|
+
),
|
|
520
|
+
)
|
|
521
|
+
continue
|
|
522
|
+
if line in wall_members:
|
|
523
|
+
continue
|
|
507
524
|
if _is_directive(body) or _is_coding_cookie(body) or line in skip:
|
|
508
525
|
continue
|
|
509
526
|
prev_body = by_line.get(line - 1)
|
|
@@ -20,6 +20,7 @@ from sarj_python_lint.rules._comments import (
|
|
|
20
20
|
nested_comment_lines,
|
|
21
21
|
restates,
|
|
22
22
|
standalone_comments,
|
|
23
|
+
statement_comment_walls,
|
|
23
24
|
)
|
|
24
25
|
from sarj_python_lint.rules._paths import is_generated
|
|
25
26
|
|
|
@@ -261,12 +262,17 @@ class NoRestatedComment(Rule):
|
|
|
261
262
|
except tokenize.TokenError, IndentationError, SyntaxError:
|
|
262
263
|
return []
|
|
263
264
|
lines = source.splitlines()
|
|
265
|
+
wall_members = frozenset(
|
|
266
|
+
line
|
|
267
|
+
for members in statement_comment_walls(path, source, standalone).values()
|
|
268
|
+
for line in members
|
|
269
|
+
)
|
|
264
270
|
diags: list[Diagnostic] = []
|
|
265
271
|
for run in comment_runs(standalone):
|
|
266
272
|
if len(run) != 1:
|
|
267
273
|
continue
|
|
268
274
|
line, col, body = run[0]
|
|
269
|
-
if line in nested:
|
|
275
|
+
if line in nested or line in wall_members:
|
|
270
276
|
continue
|
|
271
277
|
if self._restates_below(body, line, lines):
|
|
272
278
|
diags.append(
|
|
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.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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.40.0 → sarj_python_lint-0.41.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
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/no_stdlib_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.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
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.40.0 → sarj_python_lint-0.41.0}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|