pathlint 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pathlint-0.3.0/.github/workflows/test.yml +22 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/.pre-commit-config.yaml +1 -1
- {pathlint-0.2.0 → pathlint-0.3.0}/.pre-commit-hooks.yaml +2 -2
- pathlint-0.3.0/PKG-INFO +345 -0
- pathlint-0.3.0/README.md +315 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/pyproject.toml +1 -1
- pathlint-0.3.0/src/pathlint/__init__.py +13 -0
- pathlint-0.3.0/src/pathlint/__main__.py +8 -0
- pathlint-0.3.0/src/pathlint/_version.py +24 -0
- pathlint-0.3.0/src/pathlint/autofix.py +833 -0
- pathlint-0.3.0/src/pathlint/cli.py +371 -0
- pathlint-0.3.0/src/pathlint/discovery.py +312 -0
- pathlint-0.3.0/src/pathlint/linter.py +789 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/tests/test_autofix.py +74 -185
- pathlint-0.3.0/tests/test_cli.py +673 -0
- pathlint-0.3.0/tests/test_detection.py +1401 -0
- pathlint-0.3.0/tests/test_discovery.py +920 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/tests/test_linter.py +21 -32
- pathlint-0.3.0/tests/test_transformations.py +2751 -0
- pathlint-0.2.0/.github/workflows/test.yml +0 -27
- pathlint-0.2.0/PKG-INFO +0 -284
- pathlint-0.2.0/README.md +0 -254
- pathlint-0.2.0/src/pathlint/__init__.py +0 -7
- pathlint-0.2.0/src/pathlint/_version.py +0 -34
- pathlint-0.2.0/src/pathlint/autofix.py +0 -306
- pathlint-0.2.0/src/pathlint/linter.py +0 -309
- {pathlint-0.2.0 → pathlint-0.3.0}/.github/workflows/publish.yml +0 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/.gitignore +0 -0
- {pathlint-0.2.0 → pathlint-0.3.0}/LICENSE.txt +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
static:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
env:
|
|
13
|
+
PYTHONPATH: ${{ github.workspace }}/src
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
- run: python -m pip install pytest pytest-cov ruff
|
|
20
|
+
- run: ruff check .
|
|
21
|
+
- run: ruff format --check .
|
|
22
|
+
- run: python -m pytest
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
- id: pathlint
|
|
2
2
|
name: pathlint - Detect os.path usage
|
|
3
3
|
description: 'Fast linter to detect os.path usage and encourage pathlib adoption'
|
|
4
|
-
entry: pathlint
|
|
4
|
+
entry: pathlint check
|
|
5
5
|
language: python
|
|
6
6
|
types: [ python ]
|
|
7
7
|
pass_filenames: true
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- id: pathlint-fix
|
|
12
12
|
name: pathlint - Auto-fix os.path usage (experimental)
|
|
13
13
|
description: 'Automatically convert os.path to pathlib'
|
|
14
|
-
entry: pathlint
|
|
14
|
+
entry: pathlint fix
|
|
15
15
|
language: python
|
|
16
16
|
types: [ python ]
|
|
17
17
|
pass_filenames: true
|
pathlint-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pathlint
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Fast linter to detect os.path usage and encourage pathlib adoption
|
|
5
|
+
Project-URL: Repository, https://github.com/pszemraj/pathlint
|
|
6
|
+
Project-URL: Issues, https://github.com/pszemraj/pathlint/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/pszemraj/pathlint/releases
|
|
8
|
+
Project-URL: Documentation, https://github.com/pszemraj/pathlint#readme
|
|
9
|
+
Author-email: Peter Szemraj <peterszemraj+dev@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Keywords: code-quality,linter,os.path,pathlib,pre-commit,python-linter
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# pathlint
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/pathlint/)
|
|
34
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
35
|
+
|
|
36
|
+
Find `os.path` usage and migrate supported calls to `pathlib`. Pathlint has a
|
|
37
|
+
standard-library-only runtime and supports Python 3.9 and newer.
|
|
38
|
+
|
|
39
|
+
- Detect imports, aliases, function references, calls, and usage in annotations.
|
|
40
|
+
- Exclude files and directories with repeatable name or path patterns.
|
|
41
|
+
- Honor Git ignore rules automatically when Git is available.
|
|
42
|
+
- Get concise text diagnostics, quiet output, or JSON reports.
|
|
43
|
+
- Preview experimental fixes as unified diffs before applying them.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install pathlint
|
|
49
|
+
|
|
50
|
+
# Verify installation
|
|
51
|
+
pathlint --version
|
|
52
|
+
|
|
53
|
+
# Check Python files in the current directory
|
|
54
|
+
pathlint check .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Git is optional. Without Git, directory scans use built-in and explicit
|
|
58
|
+
exclusions; `.gitignore` rules are not interpreted.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Check the current directory (also the behavior of bare pathlint)
|
|
64
|
+
pathlint check
|
|
65
|
+
|
|
66
|
+
# Check one or more files or directories
|
|
67
|
+
pathlint check myfile.py
|
|
68
|
+
pathlint check src/ tests/ scripts/
|
|
69
|
+
|
|
70
|
+
# See exactly which files will be selected, without parsing them
|
|
71
|
+
pathlint files .
|
|
72
|
+
|
|
73
|
+
# Preview fixes, then apply them
|
|
74
|
+
pathlint fix src/ --dry-run
|
|
75
|
+
pathlint fix src/
|
|
76
|
+
|
|
77
|
+
# Module invocation works too
|
|
78
|
+
python -m pathlint check .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use `pathlint --help` or `pathlint check --help` for command help.
|
|
82
|
+
|
|
83
|
+
### Excluding files and directories
|
|
84
|
+
|
|
85
|
+
All three commands use the same selection rules:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pathlint check . \
|
|
89
|
+
--exclude vendor \
|
|
90
|
+
--exclude 'training/wandb/*' \
|
|
91
|
+
--exclude backups
|
|
92
|
+
|
|
93
|
+
pathlint files . -e '*.generated.py' -e 'src/legacy/'
|
|
94
|
+
pathlint fix . --exclude vendor --dry-run
|
|
95
|
+
|
|
96
|
+
# Disable Git ignores or built-in exclusions independently
|
|
97
|
+
pathlint check . --no-gitignore
|
|
98
|
+
pathlint check . --no-default-excludes
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Each `-e` / `--exclude` accepts one pattern; repeat the argument for multiple
|
|
102
|
+
patterns. Explicit exclusions add to the built-in exclusions.
|
|
103
|
+
|
|
104
|
+
- Names without `/`, such as `backups` or `test_*.py`, match at any depth.
|
|
105
|
+
- Patterns containing `/` match paths relative to the working directory.
|
|
106
|
+
`./vendor` matches the top-level `vendor`, while `vendor` matches that name
|
|
107
|
+
anywhere. Absolute patterns can exclude paths outside the working directory.
|
|
108
|
+
- Directory matches exclude their descendants. A trailing `/` restricts a
|
|
109
|
+
pattern to directories.
|
|
110
|
+
- Patterns use case-sensitive `fnmatch` syntax: `*`, `?`, and bracket expressions.
|
|
111
|
+
`*` can span `/`; `**` has no additional meaning. Quote wildcards to prevent
|
|
112
|
+
shell expansion. These patterns do not support Git-style `!` negation.
|
|
113
|
+
|
|
114
|
+
Built-in exclusions are `__pycache__`, `.venv`, `venv`, `.git`, `node_modules`,
|
|
115
|
+
`.tox`, `build`, and `dist`.
|
|
116
|
+
|
|
117
|
+
In Git worktrees, directory scans include tracked files and untracked files
|
|
118
|
+
that Git does not ignore. Git handles nested `.gitignore` rules, negation,
|
|
119
|
+
repository-local excludes, and global ignores. Tracked files remain eligible
|
|
120
|
+
even when they match an ignore rule. Nonexcluded nested repositories use their
|
|
121
|
+
own Git rules.
|
|
122
|
+
|
|
123
|
+
Explicitly named files bypass Git and built-in exclusions. **Explicit
|
|
124
|
+
`--exclude` patterns still apply**, including to filenames supplied by
|
|
125
|
+
pre-commit. File symlinks select their resolved Python targets once; exclusions
|
|
126
|
+
match both the link and target paths.
|
|
127
|
+
|
|
128
|
+
### Output
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
src/utils.py:12: PL001 os.path usage: os.path.exists(value)
|
|
132
|
+
Found 1 violation in 1 of 42 files checked.
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Paths are relative to the working directory when possible, and absolute for
|
|
136
|
+
external files. Text output retains the stream's encoding and escapes characters
|
|
137
|
+
it cannot represent, including when redirected through legacy Windows encodings.
|
|
138
|
+
Each offending source line is reported once, even if it contains several usages.
|
|
139
|
+
`PL001` means “Use pathlib instead of os.path.”
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Findings only; no summary
|
|
143
|
+
pathlint check . --quiet
|
|
144
|
+
|
|
145
|
+
# Show the five files with the most violations
|
|
146
|
+
pathlint check . --stats
|
|
147
|
+
|
|
148
|
+
# Machine-readable report
|
|
149
|
+
pathlint check . --output-format json
|
|
150
|
+
|
|
151
|
+
# Include offender statistics in the JSON report
|
|
152
|
+
pathlint check . --output-format json --stats
|
|
153
|
+
|
|
154
|
+
# Optional aggressive message
|
|
155
|
+
pathlint check . --aggressive
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
JSON reports contain `diagnostics`, `errors`, and `summary`. Each diagnostic has
|
|
159
|
+
`path`, `line`, `code`, `message`, and `source`. Summary fields are
|
|
160
|
+
`files_checked`, `files_with_issues`, `violations`, and `errors`; files that fail
|
|
161
|
+
reading or parsing are not counted as checked. `--stats` adds a `statistics`
|
|
162
|
+
list containing up to five `{path, violations}` entries.
|
|
163
|
+
|
|
164
|
+
JSON is available only for `check` and cannot be combined with `--quiet` or
|
|
165
|
+
`--aggressive`. Runtime errors are included in the JSON report without mixing
|
|
166
|
+
text into stdout. Text-mode errors go to stderr.
|
|
167
|
+
|
|
168
|
+
### Exit codes and compatibility
|
|
169
|
+
|
|
170
|
+
| Command | `0` | `1` | `2` |
|
|
171
|
+
| --- | --- | --- | --- |
|
|
172
|
+
| `check` | Clean or no files selected | Violations found | Input, discovery, read, or parse error |
|
|
173
|
+
| `fix` | Completed | — | Input, discovery, read, parse, or write error |
|
|
174
|
+
| `fix --dry-run` | No edits proposed | Edits proposed | Error |
|
|
175
|
+
| `files` | Listing completed, including empty selection | — | Input or discovery error |
|
|
176
|
+
|
|
177
|
+
Errors take precedence over violations or proposed edits. Other valid files
|
|
178
|
+
are still processed. Ctrl-C exits with status `130`.
|
|
179
|
+
|
|
180
|
+
Existing invocations continue to work without warnings:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
pathlint .
|
|
184
|
+
pathlint --fix src/
|
|
185
|
+
pathlint --dry-run src/
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The original `python -m pathlint.linter` and
|
|
189
|
+
`python -m pathlint.autofix` entry points also remain available. A valid empty
|
|
190
|
+
selection now succeeds; dry runs now return `1` when they propose edits.
|
|
191
|
+
|
|
192
|
+
## What it detects
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
import os
|
|
196
|
+
import os as system
|
|
197
|
+
import os.path as osp
|
|
198
|
+
from os import path as paths
|
|
199
|
+
from os.path import exists as present
|
|
200
|
+
|
|
201
|
+
os.path.exists("file")
|
|
202
|
+
system.path.join("directory", "file")
|
|
203
|
+
osp.isdir("directory")
|
|
204
|
+
paths.dirname(__file__)
|
|
205
|
+
present("file")
|
|
206
|
+
callback = os.path.join
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Pathlint resolves imported names in function, class, and comprehension scopes
|
|
210
|
+
so ordinary local shadowing is not mistaken for `os.path`. Private aliases in class
|
|
211
|
+
scopes are left unresolved because Python mangles their names. Deleted class
|
|
212
|
+
locals and cleared exception aliases fall back to module globals. Function and
|
|
213
|
+
generator bodies use the enclosing scope's final bindings for detection; a
|
|
214
|
+
generator's first iterable is inspected immediately. Dynamic rebinding can prevent
|
|
215
|
+
resolution. Wildcard imports invalidate tracked bindings; later explicit imports
|
|
216
|
+
can restore them. Detection inspects `if` branches independently and uses literal
|
|
217
|
+
conditions when merging bindings in the current scope. Loop bindings are merged
|
|
218
|
+
conservatively across iterations and `else` suites. Fixes in deferred bodies
|
|
219
|
+
require enclosing imports to exist when the function, lambda, or generator is
|
|
220
|
+
created, and conservatively skip aliases rebound anywhere in the file, including
|
|
221
|
+
imports with conflicting origins. Stable imports inside the body can still be
|
|
222
|
+
migrated. This is a static check, not full Python execution analysis.
|
|
223
|
+
|
|
224
|
+
Expressions in annotations are checked too. Valid `os.PathLike` annotations
|
|
225
|
+
are not `os.path` usage and are not flagged. The fixer leaves annotation
|
|
226
|
+
expressions unchanged: postponed annotations and Python 3.14's lazy annotations
|
|
227
|
+
can resolve names after their bindings change.
|
|
228
|
+
|
|
229
|
+
## Auto-fix (experimental)
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Review a unified diff without changing files
|
|
233
|
+
pathlint fix src/ --dry-run
|
|
234
|
+
|
|
235
|
+
# Apply supported migrations
|
|
236
|
+
pathlint fix src/
|
|
237
|
+
|
|
238
|
+
# Print only the diff
|
|
239
|
+
pathlint fix src/ --dry-run --quiet
|
|
240
|
+
|
|
241
|
+
# Disable terminal diff colors
|
|
242
|
+
pathlint fix src/ --dry-run --no-color
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Fixes use AST locations to change expressions while preserving surrounding
|
|
246
|
+
source text, including comments and strings. Imported aliases, named function
|
|
247
|
+
imports, calls inside nested expressions, and expressions inside f-strings are
|
|
248
|
+
supported. Pathlib imports follow module docstrings and future imports. Existing
|
|
249
|
+
Pathlib aliases are reused where possible; new constructor imports use a private
|
|
250
|
+
alias to preserve wildcard exports.
|
|
251
|
+
|
|
252
|
+
Supported calls include `isabs`, `join`, `basename`, `dirname`, and `expanduser`
|
|
253
|
+
with string-literal arguments. Calls with nonliteral arguments, including variables
|
|
254
|
+
and nested calls, remain unchanged; eligible literal calls inside them can still
|
|
255
|
+
be fixed. Values and types are not inferred.
|
|
256
|
+
Unsupported references retain their imports.
|
|
257
|
+
Keyword arguments, starred arguments, call targets containing comments, literal
|
|
258
|
+
file descriptors, byte paths, and known inexact literal forms are left unchanged.
|
|
259
|
+
Known inexact forms include empty paths, trailing separators, named-user
|
|
260
|
+
expansion, and rooted literals passed to `isabs` whose interpretation can differ
|
|
261
|
+
under Windows path rules.
|
|
262
|
+
All tilde-prefixed `expanduser` arguments remain unchanged because home values
|
|
263
|
+
can contain spellings that Pathlib normalizes or fail to resolve.
|
|
264
|
+
On Windows, string-returning fixes also skip literals whose separators or dot
|
|
265
|
+
components Pathlib would normalize.
|
|
266
|
+
Calls with uncertain branch, exception, context manager, match, or loop bindings
|
|
267
|
+
and methods whose `os.path` attributes or callable internals are assigned or
|
|
268
|
+
deleted are also left unchanged. Calls outside annotation expressions remain
|
|
269
|
+
eligible for fixes.
|
|
270
|
+
Mutation checks retain possible copies of imported modules and callables through
|
|
271
|
+
assignments, loop targets, and default parameters. They inspect all expression
|
|
272
|
+
children, conservatively including container selections and comprehensions. Calls
|
|
273
|
+
retain possible origins from their arguments; return types are not inferred.
|
|
274
|
+
Copies stored in object attributes, class fields, or container entries conservatively prevent
|
|
275
|
+
fixes for the copied module or callable. The same applies when these objects are
|
|
276
|
+
passed to calls, returned, yielded, or used by context managers or match patterns.
|
|
277
|
+
These copies do not establish bindings for detection or migration of copied calls.
|
|
278
|
+
Mutation origins are conservative across scopes and statement order; copies of
|
|
279
|
+
callable internals can also exclude fixes for their exported parent.
|
|
280
|
+
Assigning an imported module's `__class__` invalidates all of its lookups because
|
|
281
|
+
module subclasses can override attributes with descriptors.
|
|
282
|
+
Explicit `global` writes prevent affected aliases, constructors, and `str`
|
|
283
|
+
conversions from being used by fixes, including in module-level code.
|
|
284
|
+
Sources using dynamic code, namespace dictionaries, or dynamic import/attribute
|
|
285
|
+
builtins are left unchanged. This includes `exec`, `eval`, `globals`, `locals`,
|
|
286
|
+
`vars`, `__import__`, `getattr`, `setattr`, and `delattr`, their bound
|
|
287
|
+
`__setattr__`/`__delattr__` equivalents, exposed `builtins`,
|
|
288
|
+
function/frame namespaces, and module registries (`sys.modules` or
|
|
289
|
+
`importlib.import_module`); these capabilities can overwrite aliases or generated
|
|
290
|
+
constructors. Exposed function defaults, keyword defaults, and annotation dictionaries
|
|
291
|
+
also prevent fixes. Bare `dir()` use retains imports while supported calls remain eligible.
|
|
292
|
+
Fixes retain module-level imports for re-exports, including conditional imports
|
|
293
|
+
and bindings declared `global`. Imports referenced by annotations or `__all__`
|
|
294
|
+
also remain. Retained imports can still appear in check results. Unused local
|
|
295
|
+
imports can still be removed. Fixes avoid
|
|
296
|
+
constructors affected by Pathlib mutations. Sources containing wildcard imports
|
|
297
|
+
are left unchanged because
|
|
298
|
+
their exports can overwrite names used by a fix. Sources using type parameters
|
|
299
|
+
or type aliases are also left unchanged because their annotation scopes and lazy
|
|
300
|
+
values are not analyzed by the fixer. `abspath`, `splitext`, `sep`, and `pathsep`
|
|
301
|
+
are not rewritten because their former replacements changed behavior
|
|
302
|
+
or returned incorrect values. `exists`, `isfile`, and `isdir` are also left
|
|
303
|
+
unchanged: on supported Python versions, their Pathlib replacements can raise
|
|
304
|
+
filesystem errors where `os.path` returns `False`.
|
|
305
|
+
|
|
306
|
+
**Review every diff and test the result.** Pathlib normalizes path strings.
|
|
307
|
+
The fixer does not promise to remove every violation. Diff colors are enabled
|
|
308
|
+
only on interactive terminals.
|
|
309
|
+
|
|
310
|
+
## Pre-commit hook integration
|
|
311
|
+
|
|
312
|
+
Add to `.pre-commit-config.yaml`:
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
repos:
|
|
316
|
+
- repo: https://github.com/pszemraj/pathlint
|
|
317
|
+
rev: v0.3.0 # Use the release you want to pin
|
|
318
|
+
hooks:
|
|
319
|
+
- id: pathlint
|
|
320
|
+
args: [--exclude, vendor, --exclude, backups]
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Install and verify the hook:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
pre-commit install
|
|
327
|
+
pre-commit run pathlint --all-files
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The `pathlint` hook runs `pathlint check`. To apply experimental fixes instead,
|
|
331
|
+
use the `pathlint-fix` hook, which runs `pathlint fix`. Review its changes before
|
|
332
|
+
committing.
|
|
333
|
+
|
|
334
|
+
## Development
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
pip install -e '.[dev]'
|
|
338
|
+
pytest
|
|
339
|
+
ruff check .
|
|
340
|
+
ruff format --check .
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
MIT License — see [LICENSE.txt](LICENSE.txt).
|