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

andreoliwa / nitpick / 9594924093

20 Jun 2024 09:21AM CUT coverage: 96.746%. Remained the same
9594924093

push

github

andreoliwa
build(deps): bump urllib3 from 2.0.7 to 2.2.2

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.7 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.7...2.2.2)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

788 of 827 branches covered (95.28%)

Branch coverage included in aggregate %.

2096 of 2154 relevant lines covered (97.31%)

4.86 hits per line

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

93.33
/src/nitpick/plugins/base.py
1
"""Base class for file checkers."""
2

3
from __future__ import annotations
5✔
4

5
import abc
5✔
6
import fnmatch
5✔
7
from typing import TYPE_CHECKING, ClassVar, Iterator
5✔
8

9
from autorepr import autotext
5✔
10
from loguru import logger
5✔
11

12
from nitpick.blender import BaseDoc, flatten_quotes, search_json
5✔
13
from nitpick.config import SpecialConfig
5✔
14
from nitpick.constants import CONFIG_DUNDER_LIST_KEYS
5✔
15
from nitpick.typedefs import JsonDict, mypy_property
5✔
16
from nitpick.violations import Fuss, Reporter, SharedViolations
5✔
17

18
if TYPE_CHECKING:
19
    from pathlib import Path
20

21
    from marshmallow import Schema
22

23
    from nitpick.plugins.info import FileInfo
24

25

26
class NitpickPlugin(metaclass=abc.ABCMeta):  # pylint: disable=too-many-instance-attributes
5✔
27
    """Base class for Nitpick plugins.
28

29
    :param data: File information (project, path, tags).
30
    :param expected_config: Expected configuration for the file
31
    :param autofix: Flag to modify files, if the plugin supports it (default: True).
32
    """
33

34
    __str__, __unicode__ = autotext("{self.info.path_from_root} ({self.__class__.__name__})")
5✔
35

36
    filename = ""  # TODO: refactor: remove filename attribute after fixing dynamic/fixed schema loading
5✔
37
    violation_base_code: int = 0
5✔
38

39
    #: Can this plugin modify its files directly? Are the files fixable?
40
    fixable: bool = False
5✔
41

42
    #: Nested validation field for this file, to be applied in runtime when the validation schema is rebuilt.
43
    #: Useful when you have a strict configuration for a file type (e.g. :py:class:`nitpick.plugins.json.JsonPlugin`).
44
    validation_schema: Schema | None = None
5✔
45

46
    #: Which ``identify`` tags this :py:class:`nitpick.plugins.base.NitpickPlugin` child recognises.
47
    identify_tags: ClassVar = set()
5✔
48

49
    skip_empty_suggestion = False
5✔
50

51
    def __init__(self, info: FileInfo, expected_config: JsonDict, autofix=False) -> None:
5✔
52
        self.info = info
5✔
53
        self.filename = info.path_from_root
5✔
54
        self.reporter = Reporter(info, self.violation_base_code)
5✔
55

56
        self.file_path: Path = self.info.project.root / self.filename
5✔
57

58
        # Configuration for this file as a TOML dict, taken from the style file.
59
        self.expected_config: JsonDict = expected_config or {}
5✔
60

61
        self.autofix = self.fixable and autofix
5✔
62
        # Dirty flag to avoid changing files without need
63
        self.dirty: bool = False
5✔
64

65
        self._merge_special_configs()
5✔
66

67
    def _merge_special_configs(self):
5✔
68
        """Merge the predefined plugin config with the style dict to create the special config."""
69
        spc = self.predefined_special_config()
5✔
70
        temp_dict = spc.list_keys.from_plugin.copy()  # pylint: disable=no-member
5✔
71

72
        # The user can override the default list keys (if any) by setting them on the style file.
73
        # pylint: disable=assigning-non-slot,no-member
74
        spc.list_keys.from_style = self.expected_config.pop(CONFIG_DUNDER_LIST_KEYS, None) or {}
5✔
75
        temp_dict.update(flatten_quotes(spc.list_keys.from_style))
5✔
76

77
        flat_config = flatten_quotes(self.expected_config)
5✔
78

79
        for key_with_pattern, parent_child_keys in temp_dict.items():
5✔
80
            for expanded_key in fnmatch.filter(flat_config.keys(), key_with_pattern):
5✔
81
                spc.list_keys.value[expanded_key] = parent_child_keys
5✔
82

83
        self.special_config = spc
5✔
84

85
    def predefined_special_config(self) -> SpecialConfig:  # pylint: disable=no-self-use
5✔
86
        """Create a predefined special configuration for this plugin.
87

88
        Each plugin can override this method.
89
        """
90
        return SpecialConfig()
5✔
91

92
    @mypy_property
5✔
93
    def nitpick_file_dict(self) -> JsonDict:
5✔
94
        """Nitpick configuration for this file as a TOML dict, taken from the style file."""
95
        return search_json(self.info.project.nitpick_section, f'files."{self.filename}"', {})
5✔
96

97
    def entry_point(self) -> Iterator[Fuss]:
5✔
98
        """Entry point of the Nitpick plugin."""
99
        self.post_init()
5✔
100

101
        should_exist: bool = bool(self.info.project.nitpick_files_section.get(self.filename, True))
5✔
102
        if self.file_path.exists() and not should_exist:
5!
103
            logger.info(f"{self}: File {self.filename} exists when it should not")
×
104
            # Only display this message if the style is valid.
105
            yield self.reporter.make_fuss(SharedViolations.DELETE_FILE)
×
106
            return
×
107

108
        has_config_dict = bool(self.expected_config or self.nitpick_file_dict)
5✔
109
        if not has_config_dict:
5!
110
            return
×
111

112
        yield from self._enforce_file_configuration()
5✔
113

114
    def _enforce_file_configuration(self):
5✔
115
        file_exists = self.file_path.exists()
5✔
116
        if file_exists:
5✔
117
            logger.info(f"{self}: Enforcing rules")
5✔
118
            yield from self.enforce_rules()
5✔
119
        else:
120
            yield from self._suggest_when_file_not_found()
5✔
121

122
        if self.autofix and self.dirty:
5✔
123
            fuss = self.write_file(file_exists)  # pylint: disable=assignment-from-none
5✔
124
            if fuss:
5✔
125
                yield fuss
5✔
126

127
    def post_init(self):  # noqa: B027
5✔
128
        """Hook for plugin initialization after the instance was created.
129

130
        The name mimics ``__post_init__()`` on dataclasses, without the magic double underscores:
131
        `Post-init processing <https://docs.python.org/3/library/dataclasses.html#post-init-processing>`_
132
        """
133

134
    def _suggest_when_file_not_found(self):
5✔
135
        suggestion = self.initial_contents
5✔
136
        if not suggestion and self.skip_empty_suggestion:
5✔
137
            return
5✔
138
        logger.info(f"{self}: Suggest initial contents for {self.filename}")
5✔
139

140
        if suggestion:
5!
141
            yield self.reporter.make_fuss(SharedViolations.CREATE_FILE_WITH_SUGGESTION, suggestion, fixed=self.autofix)
5✔
142
        else:
143
            yield self.reporter.make_fuss(SharedViolations.CREATE_FILE)
×
144

145
    def write_file(  # pylint: disable=no-self-use
5✔
146
        self,
147
        file_exists: bool,  # pylint: disable=unused-argument # noqa: ARG002
148
    ) -> Fuss | None:
149
        """Hook to write the new file when autofix mode is on.
150

151
        Should be used by inherited classes.
152
        """
153
        return None
5✔
154

155
    @abc.abstractmethod
5✔
156
    def enforce_rules(self) -> Iterator[Fuss]:
5✔
157
        """Enforce rules for this file. It must be overridden by inherited classes if needed."""
158

159
    @property
5✔
160
    @abc.abstractmethod
5✔
161
    def initial_contents(self) -> str:
5✔
162
        """Suggested initial content when the file doesn't exist."""
163

164
    def write_initial_contents(self, doc_class: type[BaseDoc], expected_dict: dict | None = None) -> str:
5✔
165
        """Helper to write initial contents based on a format."""
166
        if not expected_dict:
5✔
167
            expected_dict = self.expected_config
5✔
168

169
        formatted_str = doc_class(obj=expected_dict).reformatted
5✔
170
        if self.autofix:
5✔
171
            self.file_path.parent.mkdir(exist_ok=True, parents=True)
5✔
172
            self.file_path.write_text(formatted_str)
5✔
173
        return formatted_str
5✔
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

© 2025 Coveralls, Inc