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

pyta-uoft / pyta / 20014220265

08 Dec 2025 01:57AM UTC coverage: 93.843% (-0.1%) from 93.944%
20014220265

Pull #1268

github

web-flow
Merge b5949d480 into 491cb20a5
Pull Request #1268: Updated to pylint and astroid v4.0 and added support for Python 3.14

17 of 17 new or added lines in 5 files covered. (100.0%)

4 existing lines in 2 files now uncovered.

3536 of 3768 relevant lines covered (93.84%)

17.84 hits per line

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

64.44
/python_ta/check/watch.py
1
"""Module to watch files for modifications and trigger PythonTA checks automatically."""
2

3
import logging
20✔
4
import os
20✔
5
import time
20✔
6
from typing import Any, Optional, Union
20✔
7

8
from pylint.lint import PyLinter
20✔
9
from watchdog.events import FileSystemEventHandler
20✔
10
from watchdog.observers import Observer
20✔
11

12
from .helpers import check_file, upload_linter_results
20✔
13

14

15
class FileChangeHandler(FileSystemEventHandler):
20✔
16
    """Internal class to handle file modifications."""
17

18
    def __init__(
20✔
19
        self,
20
        files_to_watch: set,
21
        linter: PyLinter,
22
        local_config: Union[dict[str, Any], str],
23
        load_default_config: bool,
24
        autoformat: Optional[bool],
25
        level: str,
26
        f_paths: list[str],
27
    ) -> None:
28
        self.files_to_watch = set(files_to_watch)
20✔
29
        self.linter = linter
20✔
30
        self.local_config = local_config
20✔
31
        self.load_default_config = load_default_config
20✔
32
        self.autoformat = autoformat
20✔
33
        self.level = level
20✔
34
        self.f_paths = f_paths
20✔
35

36
    def on_modified(self, event) -> None:
20✔
37
        """Trigger the callback when a watched file is modified."""
38
        if event.src_path not in self.files_to_watch:
20✔
39
            return
20✔
40

41
        logging.info(f"File modified: {event.src_path}, re-running checks...")
20✔
42

43
        current_reporter = self.linter.reporter
20✔
44
        if event.src_path in current_reporter.messages:
20✔
45
            del current_reporter.messages[event.src_path]
×
46

47
        _, self.linter = check_file(
20✔
48
            file_py=event.src_path,
49
            local_config=self.local_config,
50
            load_default_config=self.load_default_config,
51
            autoformat=self.autoformat,
52
            is_any_file_checked=True,
53
            current_reporter=current_reporter,
54
            f_paths=[],
55
        )
56
        current_reporter = self.linter.reporter
20✔
57
        current_reporter.print_messages(self.level)
20✔
58
        self.linter.generate_reports()
20✔
59
        upload_linter_results(self.linter, current_reporter, self.f_paths, self.local_config)
20✔
60

61

62
def watch_files(
20✔
63
    file_paths: set,
64
    level: str,
65
    local_config: Union[dict[str, Any], str],
66
    load_default_config: bool,
67
    autoformat: Optional[bool],
68
    linter: PyLinter,
69
    f_paths: list[str],
70
) -> None:
71
    """Watch a list of files for modifications and trigger a callback when changes occur."""
72
    directories_to_watch = {os.path.dirname(file) for file in file_paths}
×
73
    event_handler = FileChangeHandler(
×
74
        files_to_watch=file_paths,
75
        linter=linter,
76
        local_config=local_config,
77
        load_default_config=load_default_config,
78
        autoformat=autoformat,
79
        level=level,
80
        f_paths=f_paths,
81
    )
82
    observer = Observer()
×
83
    for directory in directories_to_watch:
×
84
        observer.schedule(event_handler, path=directory, recursive=False)
×
85
    logging.info("PythonTA is monitoring your files for changes and will re-check after every save")
×
86
    observer.start()
×
87

88
    try:
×
UNCOV
89
        while True:
×
90
            time.sleep(1)
×
91
    except KeyboardInterrupt:
×
92
        event_handler.linter.reporter.should_close_out = True
×
93
        event_handler.linter.reporter.on_close(event_handler.linter.stats, None)
×
94
        observer.stop()
×
95

96
    observer.join()
×
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