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

pyta-uoft / pyta / 26826362401

02 Jun 2026 02:27PM UTC coverage: 90.726% (+0.03%) from 90.695%
26826362401

Pull #1352

github

web-flow
Merge bdf9da60e into 8c247e772
Pull Request #1352: Update configuration to support `.toml` files

24 of 25 new or added lines in 1 file covered. (96.0%)

3600 of 3968 relevant lines covered (90.73%)

17.62 hits per line

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

97.53
/packages/python-ta/src/python_ta/config/__init__.py
1
"""
2
Within the config submodule, this .py file encompasses functions responsible
3
 for managing all configuration-related tasks.
4
"""
5

6
import logging
20✔
7
import os
20✔
8
import sys
20✔
9
from pathlib import Path
20✔
10
from typing import AnyStr, Optional
20✔
11

12
import toml
20✔
13
from pylint.config.config_file_parser import _ConfigurationFileParser
20✔
14
from pylint.config.config_initialization import _config_initialization
20✔
15
from pylint.config.exceptions import _UnrecognizedOptionError
20✔
16
from pylint.lint import PyLinter
20✔
17

18
DEFAULT_CONFIG_LOCATION = os.path.join("config", ".pylintrc")
20✔
19

20

21
def find_local_config(curr_dir: AnyStr) -> Optional[AnyStr]:
20✔
22
    """Search for a `.pylintrc` configuration file provided in same (user)
23
    location as the source file to check.
24
    Return absolute path to the file, or None.
25
    `curr_dir` is an absolute path to a directory, containing a file to check.
26
    For more info see, pylint.config.find_pylintrc
27
    """
28
    if curr_dir.endswith(".py"):
20✔
29
        curr_dir = os.path.dirname(curr_dir)
20✔
30
    if os.path.exists(os.path.join(curr_dir, "config", ".pylintrc")):
20✔
31
        return os.path.join(curr_dir, "config", ".pylintrc")
20✔
32
    elif os.path.exists(os.path.join(curr_dir, "config", "pylintrc")):
20✔
33
        return os.path.join(curr_dir, "config", "pylintrc")
×
34
    elif os.path.exists(os.path.join(curr_dir, "config", "pyproject.toml")):
20✔
NEW
35
        return os.path.join(curr_dir, "config", "pyproject.toml")
×
36

37

38
def load_config(linter: PyLinter, config_location: AnyStr) -> None:
20✔
39
    """Load configuration into the linter."""
40
    args_list = _get_pyta_toml_args(config_location, linter)
20✔
41
    _config_initialization(linter, args_list=args_list, config_file=config_location)
20✔
42
    linter.config_file = config_location
20✔
43

44

45
def override_config(linter: PyLinter, config_location: AnyStr) -> None:
20✔
46
    """Override the default linter configuration options (if possible).
47

48
    Snippets taken from pylint.config.config_initialization.
49
    """
50
    linter.set_current_module(config_location)
20✔
51

52
    # Read the configuration file.
53
    config_file_parser = _ConfigurationFileParser(verbose=True, linter=linter)
20✔
54
    try:
20✔
55
        _, config_args = config_file_parser.parse_config_file(file_path=config_location)
20✔
56
    except OSError as ex:
20✔
57
        logging.error(ex)
20✔
58
        sys.exit(32)
20✔
59

60
    config_args.extend(_get_pyta_toml_args(config_location, linter))
20✔
61

62
    # Override the config options by parsing the provided file.
63
    try:
20✔
64
        linter._parse_configuration_file(config_args)
20✔
65
    except _UnrecognizedOptionError as exc:
20✔
66
        unrecognized_options_message = ", ".join(exc.options)
20✔
67
        linter.add_message("unrecognized-option", args=unrecognized_options_message, line=0)
20✔
68

69
    # Everything has been set up already so emit any stashed messages.
70
    linter._emit_stashed_messages()
20✔
71

72
    linter.config_file = config_location
20✔
73

74

75
def load_messages_config(path: str, default_path: str, use_pyta_error_messages: bool) -> dict:
20✔
76
    """Given path (potentially) specified by user and default default_path
77
    of messages config file, merge the config files. We will only add the
78
    PythonTA error messages if use_pyta_error_messages is True.
79
    """
80
    # assume the user is not going to provide a path which is the same as the default
81
    if Path(default_path).resolve() == Path(path).resolve():
20✔
82
        merge_from = {}
20✔
83
    else:
84
        try:
20✔
85
            merge_from = toml.load(path)
20✔
86
        except FileNotFoundError:
20✔
87
            logging.warning(f"Could not find messages config file at {str(Path(path).resolve())}.")
20✔
88
            merge_from = {}
20✔
89

90
    # Flatten the config dictionary to get rid of section headers (if any)
91
    merge_from = flatten(merge_from)
20✔
92

93
    if not use_pyta_error_messages:
20✔
94
        return merge_from
20✔
95

96
    # Merge default pyta error messages into custom error messages
97
    merge_into = flatten(toml.load(default_path))
20✔
98
    merge_into.update(merge_from)
20✔
99
    return merge_into
20✔
100

101

102
def flatten(config_dict: dict) -> dict:
20✔
103
    """Given a nested dictionary, flatten it such that no values are themselves dictionaries."""
104
    flat_dict = {}
20✔
105
    for key, value in config_dict.items():
20✔
106
        if isinstance(value, dict):
20✔
107
            flat_dict.update(flatten(value))
20✔
108
        else:
109
            flat_dict[key] = value
20✔
110
    return flat_dict
20✔
111

112

113
def _get_pyta_toml_args(config_location: AnyStr, linter: PyLinter) -> list[str]:
20✔
114
    """Extracts [tool.python-ta] options from a TOML file and formats them as arguments."""
115
    args_list = []
20✔
116
    if not (config_location and config_location.endswith(".toml")):
20✔
117
        return args_list
20✔
118

119
    try:
20✔
120
        with open(config_location, "r") as f:
20✔
121
            config_data = toml.load(f)
20✔
122
    except OSError as ex:
20✔
123
        logging.error(ex)
20✔
124
        return args_list
20✔
125
    except toml.TomlDecodeError as ex:
20✔
126
        linter.add_message("config-parse-error", line=0, args=str(ex))
20✔
127
        return args_list
20✔
128

129
    pyta_config = config_data.get("tool", {}).get("python-ta", {})
20✔
130
    merged_pyta_config = flatten(pyta_config)
20✔
131

132
    for key, value in merged_pyta_config.items():
20✔
133
        if isinstance(value, list):
20✔
134
            args_list.extend([f"--{key}", ",".join(map(str, value))])
20✔
135
        else:
136
            args_list.extend([f"--{key}", str(value)])
20✔
137

138
    return args_list
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