• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

pyta-uoft / pyta / 15192272809

22 May 2025 04:48PM UTC coverage: 93.402% (-0.05%) from 93.449%
15192272809

Pull #1179

github

web-flow
Merge 3530c424a into 1a998eca5
Pull Request #1179: Addition of optional argument `on_verify_fail` to `check_all` and `check_error`

2 of 5 new or added lines in 1 file covered. (40.0%)

3 existing lines in 1 file now uncovered.

3440 of 3683 relevant lines covered (93.4%)

17.69 hits per line

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

94.83
/python_ta/__init__.py
1
"""Python Teaching Assistant
2

3
The goal of this module is to provide automated feedback to students in our
4
introductory Python courses, using static analysis of their code.
5

6
To run the checker, call the check function on the name of the module to check.
7

8
> import python_ta
9
> python_ta.check_all('mymodule.py')
10

11
Or, put the following code in your Python module:
12

13
if __name__ == '__main__':
14
    import python_ta
15
    python_ta.check_all()
16
"""
17

18
from __future__ import annotations
20✔
19

20
__version__ = "2.10.2.dev"  # Version number
20✔
21
# First, remove underscore from builtins if it has been bound in the REPL.
22
# Must appear before other imports from pylint/python_ta.
23
import builtins
20✔
24

25
try:
20✔
26
    del builtins._
20✔
27
except AttributeError:
20✔
28
    pass
20✔
29

30

31
import logging
20✔
32
import tokenize
20✔
33
import webbrowser
20✔
34
from typing import IO, Any, Optional, Union
20✔
35

36
from .check.helpers import (
20✔
37
    check_file,
38
    get_file_paths,
39
    get_valid_files_to_check,
40
    setup_linter,
41
    upload_linter_results,
42
    verify_pre_check,
43
)
44
from .check.watch import watch_files
20✔
45
from .config import (
20✔
46
    find_local_config,
47
    load_config,
48
    load_messages_config,
49
    override_config,
50
)
51
from .patches import patch_all
20✔
52
from .reporters import REPORTERS
20✔
53
from .reporters.core import PythonTaReporter
20✔
54
from .upload import upload_to_server
20✔
55
from .util.autoformat import run_autoformat
20✔
56

57
HELP_URL = "http://www.cs.toronto.edu/~david/pyta/checkers/index.html"
20✔
58

59

60
def check_errors(
20✔
61
    module_name: Union[list[str], str] = "",
62
    config: Union[dict[str, Any], str] = "",
63
    output: Optional[Union[str, IO]] = None,
64
    load_default_config: bool = True,
65
    autoformat: Optional[bool] = False,
66
    on_verify_fail: str = "log",
67
) -> PythonTaReporter:
68
    """Check a module for errors, printing a report."""
69
    return _check(
20✔
70
        module_name=module_name,
71
        level="error",
72
        local_config=config,
73
        output=output,
74
        load_default_config=load_default_config,
75
        autoformat=autoformat,
76
        on_verify_fail=on_verify_fail,
77
    )
78

79

80
def check_all(
20✔
81
    module_name: Union[list[str], str] = "",
82
    config: Union[dict[str, Any], str] = "",
83
    output: Optional[Union[str, IO]] = None,
84
    load_default_config: bool = True,
85
    autoformat: Optional[bool] = False,
86
    on_verify_fail: str = "log",
87
) -> PythonTaReporter:
88
    """Analyse one or more Python modules for code issues and display the results.
89

90
    Args:
91
        module_name:
92
            If an empty string (default), the module where this function is called is checked.
93
            If a non-empty string, it is interpreted as a path to a single Python module or a
94
            directory containing Python modules. If the latter, all Python modules in the directory
95
            are checked.
96
            If a list of strings, each string is interpreted as a path to a module or directory,
97
            and all modules across all paths are checked.
98
        config:
99
            If a string, a path to a configuration file to use.
100
            If a dictionary, a map of configuration options (each key is the name of an option).
101
        output:
102
            If a string, a path to a file to which the PythonTA report is written.
103
            If a typing.IO object, the report is written to this stream.
104
            If None, the report is written to standard out or automatically displayed in a
105
            web browser, depending on which reporter is used.
106
        load_default_config:
107
            If True (default), additional configuration passed with the ``config`` option is
108
            merged with the default PythonTA configuration file.
109
            If False, the default PythonTA configuration is not used.
110
        autoformat:
111
            If True, autoformat all modules using the black formatting tool before analyzing code.
112
        on_verify_fail:
113
            determines how to handle files that cannot be checked. If set to "log" (default), an error
114
            message is logged and execution continues. If set to "raise", a ValueError is raised immediately to stop
115
            execution.
116

117
    Returns:
118
        The ``PythonTaReporter`` object that generated the report.
119
    """
120
    return _check(
20✔
121
        module_name=module_name,
122
        level="all",
123
        local_config=config,
124
        output=output,
125
        load_default_config=load_default_config,
126
        autoformat=autoformat,
127
        on_verify_fail=on_verify_fail,
128
    )
129

130

131
def _check(
20✔
132
    module_name: Union[list[str], str] = "",
133
    level: str = "all",
134
    local_config: Union[dict[str, Any], str] = "",
135
    output: Optional[Union[str, IO]] = None,
136
    load_default_config: bool = True,
137
    autoformat: Optional[bool] = False,
138
    on_verify_fail: str = "log",
139
) -> PythonTaReporter:
140
    """Check a module for problems, printing a report.
141

142
    The `module_name` can take several inputs:
143
      - string of a directory, or file to check (`.py` extension optional).
144
      - list of strings of directories or files -- can have multiple.
145
      - no argument -- checks the python file containing the function call.
146
    `level` is used to specify which checks should be made.
147
    `local_config` is a dict of config options or string (config file name).
148
    `output` is an absolute or relative path to a file, or a typing.IO object to capture pyta data
149
    output. If None, stdout is used.
150
    `load_default_config` is used to specify whether to load the default .pylintrc file that comes
151
    with PythonTA. It will load it by default.
152
    `autoformat` is used to specify whether the black formatting tool is run. It is not run by default.
153
    `on_verify_fail` determines how to handle files that cannot be checked. If set to "log" (default), an error
154
     message is logged and execution continues. If set to "raise", a ValueError is raised immediately to stop execution.
155
    """
156
    # Configuring logger
157
    logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.INFO)
20✔
158
    linter, current_reporter = setup_linter(local_config, load_default_config, output)
20✔
159
    try:
20✔
160
        # Flag indicating whether at least one file has been checked
161
        is_any_file_checked = False
20✔
162
        linted_files = set()
20✔
163
        f_paths = []  # Paths to files for data submission
20✔
164
        for locations in get_valid_files_to_check(module_name):
20✔
165
            f_paths = []
20✔
166
            for file_py in get_file_paths(locations):
20✔
167
                linted_files.add(file_py)
20✔
168
                if not verify_pre_check(file_py, linter.config.allow_pylint_comments):
20✔
169
                    # File cannot be checked
170
                    if on_verify_fail == "raise":
20✔
171
                        # Flag to raise error has been chosen
172
                        raise ValueError(f"File cannot be checked: {file_py}")
20✔
173
                    # else statement not necessary, but reinforces that "log" keeps default behaviour
NEW
UNCOV
174
                    elif on_verify_fail == "log":
×
NEW
UNCOV
175
                        continue
×
176
                    # Invalid flag
177
                    else:
NEW
UNCOV
178
                        raise ValueError(f"on_verify_fail cannot take value: {on_verify_fail}")
×
179

180
                is_any_file_checked, linter = check_file(
20✔
181
                    file_py=file_py,
182
                    local_config=local_config,
183
                    load_default_config=load_default_config,
184
                    autoformat=autoformat,
185
                    is_any_file_checked=is_any_file_checked,
186
                    current_reporter=current_reporter,
187
                    f_paths=f_paths,
188
                )
189
                current_reporter = linter.reporter
20✔
190
                current_reporter.print_messages(level)
20✔
191
            upload_linter_results(linter, current_reporter, f_paths, local_config)
20✔
192
        # Only generate reports (display the webpage) if there were valid files to check
193
        if is_any_file_checked:
20✔
194
            linter.generate_reports()
20✔
195
            if linter.config.watch:
20✔
196
                watch_files(
10✔
197
                    file_paths=linted_files,
198
                    level=level,
199
                    local_config=local_config,
200
                    load_default_config=load_default_config,
201
                    autoformat=autoformat,
202
                    linter=linter,
203
                    f_paths=f_paths,
204
                )
205

206
        return current_reporter
20✔
207
    except Exception as e:
20✔
208
        logging.error(
20✔
209
            "Unexpected error encountered! Please report this to your instructor (and attach the code that caused the error)."
210
        )
211
        logging.error('Error message: "{}"'.format(e))
20✔
212
        raise e
20✔
213

214

215
def doc(msg_id: str) -> None:
20✔
216
    """Open the PythonTA documentation page for the given error message id.
217

218
    Args:
219
        msg_id: The five-character error code, e.g. ``"E0401"``.
220
    """
221
    msg_url = HELP_URL + "#" + msg_id.lower()
20✔
222
    print("Opening {} in a browser.".format(msg_url))
20✔
223
    webbrowser.open(msg_url)
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