• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pyta-uoft / pyta / 22163364181

19 Feb 2026 12:27AM UTC coverage: 89.945% (-0.01%) from 89.958%
22163364181

Pull #1305

github

web-flow
Merge d517a6e6b into b63df6c00
Pull Request #1305: Adding support for multi-line preconditions

14 of 16 new or added lines in 1 file covered. (87.5%)

2 existing lines in 1 file now uncovered.

3453 of 3839 relevant lines covered (89.95%)

17.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/packages/python-ta/src/python_ta/checkers/contract_checker.py
1
"""
2
Check for invalid syntax within function preconditons.
3
"""
4

5
import ast
20✔
6

7
from astroid import nodes
20✔
8
from pylint.checkers import BaseChecker
20✔
9
from pylint.checkers.utils import only_required_for_messages
20✔
10
from pylint.lint import PyLinter
20✔
11

12
from ..contracts import parse_assertions
20✔
13

14

15
class ContractChecker(BaseChecker):
20✔
16
    """A checker class that validates Python contract syntax."""
17

18
    name = "contract-checker"
20✔
19
    msgs = {
20✔
20
        "E9980": (
21
            "Invalid syntax in precondition: %s",
22
            "invalid-precondition-syntax",
23
            "Reported when a precondition contains invalid Python expression syntax.",
24
        )
25
    }
26

27
    @only_required_for_messages("invalid-precondition-syntax")
20✔
28
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
20✔
29
        """Visit function definition and check preconditions in docstring."""
30

31
        if not node.doc_node:
20✔
32
            return
20✔
33

34
        preconditions = parse_assertions(node, parse_token="Precondition")
20✔
35
        cleaned_conditions = self.extract_conditions(preconditions)
20✔
36

37
        for condition in cleaned_conditions:
20✔
38
            try:
20✔
39
                ast.parse(condition, mode="eval")
20✔
40
            except SyntaxError:
20✔
41
                self.add_message("invalid-precondition-syntax", node=node, args=(condition,))
20✔
42

43
    def extract_conditions(self, conditions) -> list[str]:
20✔
44
        """Return a list of conditions with multi-line conditions merged and trailing whitespaces removed"""
45
        conditions = [condition.rstrip() for condition in conditions]
20✔
46
        cleaned_conditions = []
20✔
47
        i = 0
20✔
48

49
        while i < len(conditions):
20✔
50
            condition_parts = []
20✔
51
            while i < len(conditions) and conditions[i].endswith("\\"):
20✔
NEW
UNCOV
52
                condition_parts.append(conditions[i][:-1].rstrip())
×
NEW
UNCOV
53
                i += 1
×
54
            if i < len(conditions):
20✔
55
                condition_parts.append(conditions[i])
20✔
56
            cleaned_conditions.append(" ".join(condition_parts))
20✔
57
            i += 1
20✔
58

59
        return cleaned_conditions
20✔
60

61

62
def register(linter: PyLinter) -> None:
20✔
63
    linter.register_checker(ContractChecker(linter))
20✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc