auto-walrus 0.2.2__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.
- auto_walrus-0.3.0/.github/workflows/publish_to_pypi.yml +93 -0
- auto_walrus-0.3.0/.github/workflows/tox.yml +32 -0
- auto_walrus-0.3.0/.gitignore +9 -0
- auto_walrus-0.3.0/.pre-commit-config.yaml +22 -0
- auto_walrus-0.3.0/.pre-commit-hooks.yaml +6 -0
- {auto_walrus-0.2.2/auto_walrus.egg-info → auto_walrus-0.3.0}/PKG-INFO +8 -9
- {auto_walrus-0.2.2 → auto_walrus-0.3.0}/README.md +1 -0
- {auto_walrus-0.2.2 → auto_walrus-0.3.0}/auto_walrus.py +149 -88
- auto_walrus-0.3.0/pyproject.toml +90 -0
- auto_walrus-0.3.0/requirements-dev.txt +4 -0
- auto_walrus-0.3.0/tests/__init__.py +0 -0
- auto_walrus-0.3.0/tests/main_test.py +212 -0
- auto_walrus-0.3.0/tox.ini +11 -0
- auto_walrus-0.3.0/utils/bump_version.py +26 -0
- auto_walrus-0.2.2/PKG-INFO +0 -109
- auto_walrus-0.2.2/auto_walrus.egg-info/SOURCES.txt +0 -10
- auto_walrus-0.2.2/auto_walrus.egg-info/dependency_links.txt +0 -1
- auto_walrus-0.2.2/auto_walrus.egg-info/entry_points.txt +0 -2
- auto_walrus-0.2.2/auto_walrus.egg-info/top_level.txt +0 -1
- auto_walrus-0.2.2/setup.cfg +0 -48
- auto_walrus-0.2.2/setup.py +0 -2
- {auto_walrus-0.2.2 → auto_walrus-0.3.0}/LICENSE +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
|
|
2
|
+
|
|
3
|
+
on: push
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
name: Build distribution 📦
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- name: Set up Python
|
|
13
|
+
uses: actions/setup-python@v4
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.x"
|
|
16
|
+
- name: Install pypa/build
|
|
17
|
+
run: >-
|
|
18
|
+
python3 -m
|
|
19
|
+
pip install
|
|
20
|
+
build
|
|
21
|
+
--user
|
|
22
|
+
- name: Build a binary wheel and a source tarball
|
|
23
|
+
run: python3 -m build
|
|
24
|
+
- name: Store the distribution packages
|
|
25
|
+
uses: actions/upload-artifact@v3
|
|
26
|
+
with:
|
|
27
|
+
name: python-package-distributions
|
|
28
|
+
path: dist/
|
|
29
|
+
|
|
30
|
+
publish-to-pypi:
|
|
31
|
+
name: >-
|
|
32
|
+
Publish Python 🐍 distribution 📦 to PyPI
|
|
33
|
+
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
|
|
34
|
+
needs:
|
|
35
|
+
- build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment:
|
|
38
|
+
name: pypi
|
|
39
|
+
url: https://pypi.org/p/auto-walrus # Replace <package-name> with your PyPI project name
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
- name: Download all the dists
|
|
45
|
+
uses: actions/download-artifact@v3
|
|
46
|
+
with:
|
|
47
|
+
name: python-package-distributions
|
|
48
|
+
path: dist/
|
|
49
|
+
- name: Publish distribution 📦 to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
51
|
+
|
|
52
|
+
github-release:
|
|
53
|
+
name: >-
|
|
54
|
+
Sign the Python 🐍 distribution 📦 with Sigstore
|
|
55
|
+
and upload them to GitHub Release
|
|
56
|
+
needs:
|
|
57
|
+
- publish-to-pypi
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
|
|
60
|
+
permissions:
|
|
61
|
+
contents: write # IMPORTANT: mandatory for making GitHub Releases
|
|
62
|
+
id-token: write # IMPORTANT: mandatory for sigstore
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- name: Download all the dists
|
|
66
|
+
uses: actions/download-artifact@v3
|
|
67
|
+
with:
|
|
68
|
+
name: python-package-distributions
|
|
69
|
+
path: dist/
|
|
70
|
+
- name: Sign the dists with Sigstore
|
|
71
|
+
uses: sigstore/gh-action-sigstore-python@v1.2.3
|
|
72
|
+
with:
|
|
73
|
+
inputs: >-
|
|
74
|
+
./dist/*.tar.gz
|
|
75
|
+
./dist/*.whl
|
|
76
|
+
- name: Create GitHub Release
|
|
77
|
+
env:
|
|
78
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
79
|
+
run: >-
|
|
80
|
+
gh release create
|
|
81
|
+
'${{ github.ref_name }}'
|
|
82
|
+
--repo '${{ github.repository }}'
|
|
83
|
+
--notes ""
|
|
84
|
+
- name: Upload artifact signatures to GitHub Release
|
|
85
|
+
env:
|
|
86
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
87
|
+
# Upload to GitHub Release using the `gh` CLI.
|
|
88
|
+
# `dist/` contains the built packages, and the
|
|
89
|
+
# sigstore-produced signatures and certificates.
|
|
90
|
+
run: >-
|
|
91
|
+
gh release upload
|
|
92
|
+
'${{ github.ref_name }}' dist/**
|
|
93
|
+
--repo '${{ github.repository }}'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: tox
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
tox:
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
|
13
|
+
os: [windows-latest, ubuntu-latest]
|
|
14
|
+
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v3
|
|
18
|
+
- uses: actions/setup-python@v4
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Cache multiple paths
|
|
22
|
+
uses: actions/cache@v3
|
|
23
|
+
with:
|
|
24
|
+
path: |
|
|
25
|
+
~/.cache/pip
|
|
26
|
+
$RUNNER_TOOL_CACHE/Python/*
|
|
27
|
+
~\AppData\Local\pip\Cache
|
|
28
|
+
key: ${{ runner.os }}-build-${{ matrix.python-version }}
|
|
29
|
+
- name: install-tox
|
|
30
|
+
run: python -m pip install --upgrade tox virtualenv setuptools pip
|
|
31
|
+
- name: run-tox
|
|
32
|
+
run: tox -e py
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
# Ruff version.
|
|
4
|
+
rev: 'v0.3.5'
|
|
5
|
+
hooks:
|
|
6
|
+
# Run the formatter.
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
# Run the linter.
|
|
9
|
+
- id: ruff
|
|
10
|
+
args: [--fix]
|
|
11
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
12
|
+
rev: 'v1.9.0'
|
|
13
|
+
hooks:
|
|
14
|
+
- id: mypy
|
|
15
|
+
additional_dependencies: [pytest]
|
|
16
|
+
- repo: https://github.com/codespell-project/codespell
|
|
17
|
+
rev: 'v2.2.6'
|
|
18
|
+
hooks:
|
|
19
|
+
- id: codespell
|
|
20
|
+
files: \.(py|rst|md)$
|
|
21
|
+
args: [--ignore-words-list=ser]
|
|
22
|
+
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: auto-walrus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Automatically apply the awesome walrus operator
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Project-URL: Homepage, https://github.com/MarcoGorelli/auto-walrus
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/MarcoGorelli/auto-walrus
|
|
7
|
+
Author-email: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com>
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
9
11
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
13
12
|
Requires-Python: >=3.8
|
|
14
13
|
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
14
|
|
|
17
15
|
<h1 align="center">
|
|
18
16
|
auto-walrus
|
|
@@ -83,6 +81,7 @@ To my great surprise, this is being used by:
|
|
|
83
81
|
- https://github.com/python-graphblas/python-graphblas
|
|
84
82
|
- https://github.com/Remi-Gau/bids2cite
|
|
85
83
|
- https://github.com/TheAlgorithms/Python
|
|
84
|
+
- https://github.com/apache/superset
|
|
86
85
|
|
|
87
86
|
Anyone else? Please let me know, or you can open a pull request to add yourself.
|
|
88
87
|
|
|
@@ -67,6 +67,7 @@ To my great surprise, this is being used by:
|
|
|
67
67
|
- https://github.com/python-graphblas/python-graphblas
|
|
68
68
|
- https://github.com/Remi-Gau/bids2cite
|
|
69
69
|
- https://github.com/TheAlgorithms/Python
|
|
70
|
+
- https://github.com/apache/superset
|
|
70
71
|
|
|
71
72
|
Anyone else? Please let me know, or you can open a pull request to add yourself.
|
|
72
73
|
|
|
@@ -2,17 +2,32 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import ast
|
|
5
|
+
import os
|
|
6
|
+
import pathlib
|
|
5
7
|
import re
|
|
6
8
|
import sys
|
|
9
|
+
from typing import Any
|
|
7
10
|
from typing import Iterable
|
|
8
11
|
from typing import Sequence
|
|
9
12
|
from typing import Tuple
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
if sys.version_info >= (3, 11): # pragma: no cover
|
|
15
|
+
import tomllib
|
|
16
|
+
else: # pragma: no cover
|
|
17
|
+
import tomli as tomllib
|
|
18
|
+
|
|
19
|
+
SEP_SYMBOLS = frozenset(("(", ")", ",", ":"))
|
|
12
20
|
# name, lineno, col_offset, end_lineno, end_col_offset
|
|
13
21
|
Token = Tuple[str, int, int, int, int]
|
|
14
22
|
SIMPLE_NODE = (ast.Name, ast.Constant)
|
|
15
|
-
ENDS_WITH_COMMENT = re.compile(r
|
|
23
|
+
ENDS_WITH_COMMENT = re.compile(r"#.*$")
|
|
24
|
+
EXCLUDES = (
|
|
25
|
+
r"/("
|
|
26
|
+
r"\.direnv|\.eggs|\.git|\.hg|\.ipynb_checkpoints|\.mypy_cache|\.nox|\.svn|"
|
|
27
|
+
r"\.tox|\.venv|"
|
|
28
|
+
r"_build|buck-out|build|dist|venv"
|
|
29
|
+
r")/"
|
|
30
|
+
)
|
|
16
31
|
|
|
17
32
|
|
|
18
33
|
def name_lineno_coloffset_iterable(
|
|
@@ -26,18 +41,10 @@ def name_lineno_coloffset(tokens: Token) -> tuple[str, int, int]:
|
|
|
26
41
|
|
|
27
42
|
|
|
28
43
|
def is_simple_test(node: ast.AST) -> bool:
|
|
29
|
-
return (
|
|
30
|
-
isinstance(node,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
and isinstance(node.left, SIMPLE_NODE)
|
|
34
|
-
and (
|
|
35
|
-
all(
|
|
36
|
-
isinstance(_node, SIMPLE_NODE)
|
|
37
|
-
for _node in node.comparators
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
)
|
|
44
|
+
return isinstance(node, SIMPLE_NODE) or (
|
|
45
|
+
isinstance(node, ast.Compare)
|
|
46
|
+
and isinstance(node.left, SIMPLE_NODE)
|
|
47
|
+
and (all(isinstance(_node, SIMPLE_NODE) for _node in node.comparators))
|
|
41
48
|
)
|
|
42
49
|
|
|
43
50
|
|
|
@@ -75,7 +82,9 @@ def find_names(
|
|
|
75
82
|
if isinstance(_node, ast.Name):
|
|
76
83
|
names.add(
|
|
77
84
|
record_name_lineno_coloffset(
|
|
78
|
-
_node,
|
|
85
|
+
_node,
|
|
86
|
+
end_lineno,
|
|
87
|
+
end_col_offset,
|
|
79
88
|
),
|
|
80
89
|
)
|
|
81
90
|
return names
|
|
@@ -97,14 +106,13 @@ def process_assign(
|
|
|
97
106
|
assignments: set[Token],
|
|
98
107
|
related_vars: dict[str, list[Token]],
|
|
99
108
|
) -> None:
|
|
100
|
-
if (
|
|
101
|
-
len(node.targets) == 1
|
|
102
|
-
and isinstance(node.targets[0], ast.Name)
|
|
103
|
-
):
|
|
109
|
+
if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
|
|
104
110
|
target = node.targets[0]
|
|
105
111
|
assignments.add(
|
|
106
112
|
record_name_lineno_coloffset(
|
|
107
|
-
target,
|
|
113
|
+
target,
|
|
114
|
+
node.end_lineno,
|
|
115
|
+
node.end_col_offset,
|
|
108
116
|
),
|
|
109
117
|
)
|
|
110
118
|
related_vars[target.id] = list(find_names(node.value))
|
|
@@ -123,36 +131,38 @@ def is_walrussable(
|
|
|
123
131
|
) -> bool:
|
|
124
132
|
return (
|
|
125
133
|
# check name doesn't appear between assignment and if statement
|
|
126
|
-
_assignment[0]
|
|
127
|
-
|
|
128
|
-
for i in range(assignment_idx+1, if_statement_idx)
|
|
129
|
-
]
|
|
134
|
+
_assignment[0]
|
|
135
|
+
not in [sorted_names[i][0] for i in range(assignment_idx + 1, if_statement_idx)]
|
|
130
136
|
# check it's the variable's only assignment
|
|
131
137
|
and (len(_other_assignments) == 1)
|
|
132
138
|
# check this is the first usage of this name
|
|
133
139
|
and (
|
|
134
140
|
name_lineno_coloffset(
|
|
135
141
|
_other_usages[0],
|
|
136
|
-
)
|
|
142
|
+
)
|
|
143
|
+
== name_lineno_coloffset(_assignment)
|
|
137
144
|
)
|
|
138
145
|
# check it's used at least somewhere else
|
|
139
146
|
and (len(_other_usages) > 2)
|
|
140
147
|
# check it doesn't appear anywhere else
|
|
141
148
|
and not [
|
|
142
|
-
i
|
|
149
|
+
i
|
|
150
|
+
for i in names
|
|
143
151
|
if (
|
|
144
|
-
name_lineno_coloffset(i)
|
|
145
|
-
name_lineno_coloffset_iterable(in_body_vars[_if_statement])
|
|
152
|
+
name_lineno_coloffset(i)
|
|
153
|
+
not in name_lineno_coloffset_iterable(in_body_vars[_if_statement])
|
|
146
154
|
)
|
|
147
155
|
and (
|
|
148
156
|
name_lineno_coloffset(
|
|
149
157
|
i,
|
|
150
|
-
)
|
|
158
|
+
)
|
|
159
|
+
!= name_lineno_coloffset(_assignment)
|
|
151
160
|
)
|
|
152
161
|
and (
|
|
153
162
|
name_lineno_coloffset(
|
|
154
163
|
i,
|
|
155
|
-
)
|
|
164
|
+
)
|
|
165
|
+
!= name_lineno_coloffset(_if_statement)
|
|
156
166
|
)
|
|
157
167
|
and i[0] == _assignment[0]
|
|
158
168
|
]
|
|
@@ -171,10 +181,7 @@ def related_vars_are_unused(
|
|
|
171
181
|
related = related_vars[name]
|
|
172
182
|
should_break = False
|
|
173
183
|
for rel in related:
|
|
174
|
-
usages = [
|
|
175
|
-
i for i in sorted_names if i[0]
|
|
176
|
-
== rel[0] if i != rel
|
|
177
|
-
]
|
|
184
|
+
usages = [i for i in sorted_names if i[0] == rel[0] if i != rel]
|
|
178
185
|
for usage in usages:
|
|
179
186
|
rel_used_idx = name_lineno_coloffset_iterable(
|
|
180
187
|
sorted_names,
|
|
@@ -186,7 +193,6 @@ def related_vars_are_unused(
|
|
|
186
193
|
|
|
187
194
|
def visit_function_def(
|
|
188
195
|
node: ast.FunctionDef,
|
|
189
|
-
path: str,
|
|
190
196
|
) -> list[tuple[Token, Token]]:
|
|
191
197
|
names = set()
|
|
192
198
|
assignments: set[Token] = set()
|
|
@@ -224,13 +230,8 @@ def visit_function_def(
|
|
|
224
230
|
if_statement_idx = name_lineno_coloffset_iterable(
|
|
225
231
|
sorted_names,
|
|
226
232
|
).index(name_lineno_coloffset(_if_statement))
|
|
227
|
-
_other_assignments = [
|
|
228
|
-
|
|
229
|
-
for i in sorted_assignments if i[0] == _assignment[0]
|
|
230
|
-
]
|
|
231
|
-
_other_usages = [
|
|
232
|
-
i for i in sorted_names if i[0] == _assignment[0]
|
|
233
|
-
]
|
|
233
|
+
_other_assignments = [i for i in sorted_assignments if i[0] == _assignment[0]]
|
|
234
|
+
_other_usages = [i for i in sorted_names if i[0] == _assignment[0]]
|
|
234
235
|
if is_walrussable(
|
|
235
236
|
_assignment,
|
|
236
237
|
_if_statement,
|
|
@@ -241,19 +242,21 @@ def visit_function_def(
|
|
|
241
242
|
_other_usages,
|
|
242
243
|
names,
|
|
243
244
|
in_body_vars,
|
|
245
|
+
) and related_vars_are_unused(
|
|
246
|
+
related_vars,
|
|
247
|
+
_assignment[0],
|
|
248
|
+
sorted_names,
|
|
249
|
+
assignment_idx,
|
|
250
|
+
if_statement_idx,
|
|
244
251
|
):
|
|
245
|
-
|
|
246
|
-
related_vars,
|
|
247
|
-
_assignment[0],
|
|
248
|
-
sorted_names,
|
|
249
|
-
assignment_idx,
|
|
250
|
-
if_statement_idx,
|
|
251
|
-
):
|
|
252
|
-
walrus.append((_assignment, _if_statement))
|
|
252
|
+
walrus.append((_assignment, _if_statement))
|
|
253
253
|
return walrus
|
|
254
254
|
|
|
255
255
|
|
|
256
|
-
def auto_walrus(
|
|
256
|
+
def auto_walrus(
|
|
257
|
+
content: str,
|
|
258
|
+
line_length: int,
|
|
259
|
+
) -> str | None:
|
|
257
260
|
lines = content.splitlines()
|
|
258
261
|
try:
|
|
259
262
|
tree = ast.parse(content)
|
|
@@ -263,7 +266,7 @@ def auto_walrus(content: str, path: str, line_length: int) -> str | None:
|
|
|
263
266
|
walruses = []
|
|
264
267
|
for node in ast.walk(tree):
|
|
265
268
|
if isinstance(node, ast.FunctionDef):
|
|
266
|
-
walruses.extend(visit_function_def(node
|
|
269
|
+
walruses.extend(visit_function_def(node))
|
|
267
270
|
lines_to_remove = []
|
|
268
271
|
walruses = sorted(walruses, key=lambda x: (-x[1][1], -x[1][2]))
|
|
269
272
|
|
|
@@ -273,75 +276,133 @@ def auto_walrus(content: str, path: str, line_length: int) -> str | None:
|
|
|
273
276
|
for _assignment, _if_statement in walruses:
|
|
274
277
|
if _assignment[1] != _assignment[3]:
|
|
275
278
|
continue
|
|
276
|
-
txt = lines[_assignment[1]-1][_assignment[2]:_assignment[4]]
|
|
277
|
-
if txt.count(
|
|
279
|
+
txt = lines[_assignment[1] - 1][_assignment[2] : _assignment[4]]
|
|
280
|
+
if txt.count("=") > 1:
|
|
278
281
|
continue
|
|
279
|
-
line = lines[_if_statement[1]-1]
|
|
280
|
-
left_bit = line[:_if_statement[2]]
|
|
281
|
-
right_bit = line[_if_statement[4]:]
|
|
282
|
+
line = lines[_if_statement[1] - 1]
|
|
283
|
+
left_bit = line[: _if_statement[2]]
|
|
284
|
+
right_bit = line[_if_statement[4] :]
|
|
282
285
|
no_paren = any(left_bit.endswith(i) for i in SEP_SYMBOLS) and any(
|
|
283
286
|
right_bit.startswith(i) for i in SEP_SYMBOLS
|
|
284
287
|
)
|
|
285
|
-
replace = txt.replace(
|
|
288
|
+
replace = txt.replace("=", ":=")
|
|
286
289
|
if no_paren:
|
|
287
290
|
line_with_walrus = left_bit + replace + right_bit
|
|
288
291
|
else:
|
|
289
|
-
line_with_walrus = left_bit +
|
|
292
|
+
line_with_walrus = left_bit + "(" + replace + ")" + right_bit
|
|
290
293
|
if len(line_with_walrus) > line_length:
|
|
291
294
|
# don't rewrite if it would split over multiple lines
|
|
292
295
|
continue
|
|
293
296
|
# replace assignment
|
|
294
297
|
line_without_assignment = (
|
|
295
|
-
f
|
|
296
|
-
f
|
|
298
|
+
f"{lines[_assignment[1]-1][:_assignment[2]]}"
|
|
299
|
+
f"{lines[_assignment[1]-1][_assignment[4]:]}"
|
|
297
300
|
)
|
|
298
|
-
if (
|
|
299
|
-
ENDS_WITH_COMMENT.search(lines[
|
|
300
|
-
) or (
|
|
301
|
-
ENDS_WITH_COMMENT.search(lines[_if_statement[1]-1]) is not None
|
|
301
|
+
if (ENDS_WITH_COMMENT.search(lines[_assignment[1] - 1]) is not None) or (
|
|
302
|
+
ENDS_WITH_COMMENT.search(lines[_if_statement[1] - 1]) is not None
|
|
302
303
|
):
|
|
303
304
|
continue
|
|
304
305
|
lines[_assignment[1] - 1] = line_without_assignment
|
|
305
306
|
# add walrus
|
|
306
|
-
lines[_if_statement[1]-1] = line_with_walrus
|
|
307
|
+
lines[_if_statement[1] - 1] = line_with_walrus
|
|
307
308
|
# remove empty line
|
|
308
|
-
if not lines[_assignment[1]-1].strip():
|
|
309
|
-
lines_to_remove.append(_assignment[1]-1)
|
|
309
|
+
if not lines[_assignment[1] - 1].strip():
|
|
310
|
+
lines_to_remove.append(_assignment[1] - 1)
|
|
310
311
|
|
|
311
312
|
newlines = [
|
|
312
|
-
line
|
|
313
|
+
line
|
|
314
|
+
for i, line in enumerate(
|
|
313
315
|
lines,
|
|
314
|
-
)
|
|
316
|
+
)
|
|
317
|
+
if i not in lines_to_remove
|
|
315
318
|
]
|
|
316
|
-
newcontent =
|
|
317
|
-
if newcontent and content.endswith(
|
|
318
|
-
newcontent +=
|
|
319
|
+
newcontent = "\n".join(newlines)
|
|
320
|
+
if newcontent and content.endswith("\n"):
|
|
321
|
+
newcontent += "\n"
|
|
319
322
|
if newcontent != content:
|
|
320
323
|
return newcontent
|
|
321
324
|
return None
|
|
322
325
|
|
|
323
326
|
|
|
327
|
+
def _get_config(paths: list[pathlib.Path]) -> dict[str, Any]:
|
|
328
|
+
"""Get the configuration from a config file.
|
|
329
|
+
|
|
330
|
+
Search for a pyproject.toml in common parent directories
|
|
331
|
+
of the given list of paths.
|
|
332
|
+
"""
|
|
333
|
+
root = pathlib.Path(os.path.commonpath(paths))
|
|
334
|
+
root = root.parent if root.is_file() else root
|
|
335
|
+
|
|
336
|
+
while root != root.parent:
|
|
337
|
+
config_file = root / "pyproject.toml"
|
|
338
|
+
if config_file.is_file():
|
|
339
|
+
config = tomllib.loads(config_file.read_text())
|
|
340
|
+
config = config.get("tool", {}).get("auto-walrus", {})
|
|
341
|
+
if config:
|
|
342
|
+
return config
|
|
343
|
+
|
|
344
|
+
root = root.parent
|
|
345
|
+
|
|
346
|
+
return {}
|
|
347
|
+
|
|
348
|
+
|
|
324
349
|
def main(argv: Sequence[str] | None = None) -> int: # pragma: no cover
|
|
325
350
|
parser = argparse.ArgumentParser()
|
|
326
|
-
parser.add_argument(
|
|
351
|
+
parser.add_argument("paths", nargs="*")
|
|
352
|
+
parser.add_argument(
|
|
353
|
+
"--files",
|
|
354
|
+
help="Regex pattern with which to match files to include",
|
|
355
|
+
required=False,
|
|
356
|
+
default=r"",
|
|
357
|
+
)
|
|
358
|
+
parser.add_argument(
|
|
359
|
+
"--exclude",
|
|
360
|
+
help="Regex pattern with which to match files to exclude",
|
|
361
|
+
required=False,
|
|
362
|
+
default=r"^$",
|
|
363
|
+
)
|
|
327
364
|
# black formatter's default
|
|
328
|
-
parser.add_argument(
|
|
365
|
+
parser.add_argument("--line-length", type=int, default=88)
|
|
329
366
|
args = parser.parse_args(argv)
|
|
367
|
+
paths = [pathlib.Path(path).resolve() for path in args.paths]
|
|
368
|
+
|
|
369
|
+
# Update defaults from pyproject.toml if present
|
|
370
|
+
config = {k.replace("-", "_"): v for k, v in _get_config(paths).items()}
|
|
371
|
+
parser.set_defaults(**config)
|
|
372
|
+
args = parser.parse_args(argv)
|
|
373
|
+
|
|
330
374
|
ret = 0
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
375
|
+
|
|
376
|
+
for path in paths:
|
|
377
|
+
if path.is_file():
|
|
378
|
+
filepaths = iter((path,))
|
|
379
|
+
else:
|
|
380
|
+
filepaths = (
|
|
381
|
+
p
|
|
382
|
+
for p in path.rglob("*")
|
|
383
|
+
if re.search(args.files, p.as_posix(), re.VERBOSE)
|
|
384
|
+
and not re.search(args.exclude, p.as_posix(), re.VERBOSE)
|
|
385
|
+
and not re.search(EXCLUDES, p.as_posix())
|
|
386
|
+
and p.suffix == ".py"
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
for filepath in filepaths:
|
|
390
|
+
try:
|
|
391
|
+
with open(filepath, encoding="utf-8") as fd:
|
|
392
|
+
content = fd.read()
|
|
393
|
+
except UnicodeDecodeError:
|
|
394
|
+
continue
|
|
395
|
+
new_content = auto_walrus(
|
|
396
|
+
content,
|
|
397
|
+
line_length=args.line_length,
|
|
398
|
+
)
|
|
399
|
+
if new_content is not None and content != new_content:
|
|
400
|
+
sys.stdout.write(f"Rewriting {filepath}\n")
|
|
401
|
+
with open(filepath, "w", encoding="utf-8") as fd:
|
|
402
|
+
fd.write(new_content)
|
|
403
|
+
ret = 1
|
|
343
404
|
return ret
|
|
344
405
|
|
|
345
406
|
|
|
346
|
-
if __name__ ==
|
|
407
|
+
if __name__ == "__main__":
|
|
347
408
|
sys.exit(main())
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "auto-walrus"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Marco Gorelli", email="33491632+MarcoGorelli@users.noreply.github.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Automatically apply the awesome walrus operator"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
"Homepage" = "https://github.com/MarcoGorelli/auto-walrus"
|
|
22
|
+
"Bug Tracker" = "https://github.com/MarcoGorelli/auto-walrus"
|
|
23
|
+
|
|
24
|
+
[tool.ruff]
|
|
25
|
+
line-length = 90
|
|
26
|
+
fix = true
|
|
27
|
+
target-version = "py38"
|
|
28
|
+
|
|
29
|
+
lint.select = [
|
|
30
|
+
"ALL",
|
|
31
|
+
]
|
|
32
|
+
lint.ignore = [
|
|
33
|
+
'A001',
|
|
34
|
+
'A003',
|
|
35
|
+
'ANN101',
|
|
36
|
+
'ANN401',
|
|
37
|
+
'ARG002', # todo: enable
|
|
38
|
+
'ARG003', # todo: enable
|
|
39
|
+
'C901',
|
|
40
|
+
'COM812',
|
|
41
|
+
'D',
|
|
42
|
+
'DTZ',
|
|
43
|
+
'E501',
|
|
44
|
+
'EM101', # todo: enable
|
|
45
|
+
'ERA001', # todo: enable
|
|
46
|
+
'FBT003', # todo: enable
|
|
47
|
+
'FIX',
|
|
48
|
+
'ICN001',
|
|
49
|
+
'ISC001',
|
|
50
|
+
'PD',
|
|
51
|
+
'PLR0911',
|
|
52
|
+
'PLR0912',
|
|
53
|
+
'PLR5501',
|
|
54
|
+
'PLR2004',
|
|
55
|
+
'PT011',
|
|
56
|
+
'PTH',
|
|
57
|
+
'RET505',
|
|
58
|
+
'S',
|
|
59
|
+
'SLF001',
|
|
60
|
+
'TD',
|
|
61
|
+
'TRY003', # todo: enable
|
|
62
|
+
'TRY004'
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.isort]
|
|
66
|
+
force-single-line = true
|
|
67
|
+
|
|
68
|
+
[tool.black]
|
|
69
|
+
line-length = 90
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
filterwarnings = [
|
|
73
|
+
"error",
|
|
74
|
+
'ignore:distutils Version classes are deprecated:DeprecationWarning',
|
|
75
|
+
]
|
|
76
|
+
xfail_strict = true
|
|
77
|
+
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
|
|
78
|
+
|
|
79
|
+
[tool.coverage.run]
|
|
80
|
+
plugins = ["covdefaults"]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.report]
|
|
83
|
+
exclude_also = [
|
|
84
|
+
"> POLARS_VERSION",
|
|
85
|
+
"if sys.version_info() <",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.mypy]
|
|
89
|
+
strict = true
|
|
90
|
+
|
|
File without changes
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from auto_walrus import auto_walrus
|
|
11
|
+
from auto_walrus import main
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize(
|
|
15
|
+
("src", "expected"),
|
|
16
|
+
[
|
|
17
|
+
(
|
|
18
|
+
"def foo():\n" " a = 0\n" " if a:\n" " print(a)\n",
|
|
19
|
+
"def foo():\n" " if (a := 0):\n" " print(a)\n",
|
|
20
|
+
),
|
|
21
|
+
(
|
|
22
|
+
"def foo():\n" " a = 0\n" " if a > 3:\n" " print(a)\n",
|
|
23
|
+
"def foo():\n" " if (a := 0) > 3:\n" " print(a)\n",
|
|
24
|
+
),
|
|
25
|
+
(
|
|
26
|
+
"def foo():\n"
|
|
27
|
+
" a = 0\n"
|
|
28
|
+
" if a:\n"
|
|
29
|
+
" print(a)\n"
|
|
30
|
+
" else:\n"
|
|
31
|
+
" pass\n",
|
|
32
|
+
"def foo():\n"
|
|
33
|
+
" if (a := 0):\n"
|
|
34
|
+
" print(a)\n"
|
|
35
|
+
" else:\n"
|
|
36
|
+
" pass\n",
|
|
37
|
+
),
|
|
38
|
+
(
|
|
39
|
+
"def foo():\n"
|
|
40
|
+
" a = 0\n"
|
|
41
|
+
" if True:\n"
|
|
42
|
+
" print(1)\n"
|
|
43
|
+
" elif a:\n"
|
|
44
|
+
" print(a)\n",
|
|
45
|
+
"def foo():\n"
|
|
46
|
+
" if True:\n"
|
|
47
|
+
" print(1)\n"
|
|
48
|
+
" elif (a := 0):\n"
|
|
49
|
+
" print(a)\n",
|
|
50
|
+
),
|
|
51
|
+
(
|
|
52
|
+
"def foo():\n"
|
|
53
|
+
" a = 0\n"
|
|
54
|
+
" print(0)\n"
|
|
55
|
+
" if a:\n"
|
|
56
|
+
" print(a)\n",
|
|
57
|
+
"def foo():\n" " print(0)\n" " if (a := 0):\n" " print(a)\n",
|
|
58
|
+
),
|
|
59
|
+
(
|
|
60
|
+
"def foo():\n" " a = 0\n" " if (a):\n" " print(a)\n",
|
|
61
|
+
"def foo():\n" " if (a := 0):\n" " print(a)\n",
|
|
62
|
+
),
|
|
63
|
+
(
|
|
64
|
+
"def foo():\n" " b = 0; a = 0\n" " if a:\n" " print(a)\n",
|
|
65
|
+
"def foo():\n" " b = 0; \n" " if (a := 0):\n" " print(a)\n",
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"def foo():\n" " a = 0\n" " if a:\n" " print(a)",
|
|
69
|
+
"def foo():\n" " if (a := 0):\n" " print(a)",
|
|
70
|
+
),
|
|
71
|
+
(
|
|
72
|
+
"def foo():\n"
|
|
73
|
+
" a = 0\n"
|
|
74
|
+
" if a:\n"
|
|
75
|
+
" print(a)\n"
|
|
76
|
+
" if (b := 3) > 0:\n"
|
|
77
|
+
" print(b)\n",
|
|
78
|
+
"def foo():\n"
|
|
79
|
+
" if (a := 0):\n"
|
|
80
|
+
" print(a)\n"
|
|
81
|
+
" if (b := 3) > 0:\n"
|
|
82
|
+
" print(b)\n",
|
|
83
|
+
),
|
|
84
|
+
(
|
|
85
|
+
"def foo():\n"
|
|
86
|
+
" a = 0\n"
|
|
87
|
+
" if np.sin(b) + np.cos(b) < np.tan(b):\n"
|
|
88
|
+
" pass\n"
|
|
89
|
+
" elif a:\n"
|
|
90
|
+
" print(a)\n",
|
|
91
|
+
"def foo():\n"
|
|
92
|
+
" if np.sin(b) + np.cos(b) < np.tan(b):\n"
|
|
93
|
+
" pass\n"
|
|
94
|
+
" elif (a := 0):\n"
|
|
95
|
+
" print(a)\n",
|
|
96
|
+
),
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
def test_rewrite(src: str, expected: str) -> None:
|
|
100
|
+
ret = auto_walrus(src, 88)
|
|
101
|
+
assert ret == expected
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.mark.parametrize(
|
|
105
|
+
"src",
|
|
106
|
+
[
|
|
107
|
+
"def foo():\n"
|
|
108
|
+
" b = [0]\n"
|
|
109
|
+
" a = b[0]\n"
|
|
110
|
+
" b[0] = 1\n"
|
|
111
|
+
" if a:\n"
|
|
112
|
+
" print(a)\n",
|
|
113
|
+
"def foo():\n" " a = 1\n" " a = 2\n" " if a:\n" " print(a)\n",
|
|
114
|
+
"def foo():\n" " a = (\n" " 0,)\n" " if a:\n" " print(a)\n",
|
|
115
|
+
"def foo():\n" " a = (b==True)\n" " if a:\n" " print(a)\n",
|
|
116
|
+
"def foo():\n"
|
|
117
|
+
" a = thequickbrownfoxjumpsoverthelazydog\n"
|
|
118
|
+
" if a:\n"
|
|
119
|
+
" print(a)\n",
|
|
120
|
+
"def foo():\n" " a = 0 # no-walrus\n" " if a:\n" " print(a)\n",
|
|
121
|
+
"def foo():\n" " a = 0\n" " if a: # no-walrus\n" " print(a)\n",
|
|
122
|
+
"n = 10\n" "if foo(a := n+1):\n" " print(n)\n",
|
|
123
|
+
"a = 0\n" "if False and a:\n" " print(a)\n" "else:\n" " print(a)\n",
|
|
124
|
+
"def foo():\n" " a = 1\n" " if a:\n" " print(a)\n" " a = 2\n",
|
|
125
|
+
"def foo():\n"
|
|
126
|
+
" n = 10\n"
|
|
127
|
+
" if True:\n"
|
|
128
|
+
" pass\n"
|
|
129
|
+
" elif foo(a := n+1):\n"
|
|
130
|
+
" print(n)\n",
|
|
131
|
+
"def foo():\n"
|
|
132
|
+
" n = 10\n"
|
|
133
|
+
" if n > np.sin(foo.bar.quox):\n"
|
|
134
|
+
" print(n)\n",
|
|
135
|
+
"def foo():\n" " n = 10\n" " if True or n > 3:\n" " print(n)\n",
|
|
136
|
+
],
|
|
137
|
+
)
|
|
138
|
+
def test_noop(src: str) -> None:
|
|
139
|
+
ret = auto_walrus(src, 40)
|
|
140
|
+
assert ret is None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
ProjectDirT = Tuple[pathlib.Path, List[pathlib.Path]]
|
|
144
|
+
|
|
145
|
+
SRC_ORIG = "def foo():\n" " a = 0\n" " if a:\n" " print(a)\n"
|
|
146
|
+
SRC_CHANGED = "def foo():\n" " if (a := 0):\n" " print(a)\n"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@pytest.fixture()
|
|
150
|
+
def project_dir(request: Any, tmp_path: pathlib.Path) -> ProjectDirT:
|
|
151
|
+
# tmp_path will be the root of the project, e.g.:
|
|
152
|
+
# tmp_path
|
|
153
|
+
# ├── submodule1/
|
|
154
|
+
# | ├── submodule2/
|
|
155
|
+
# | | └── a.py
|
|
156
|
+
# | └── b.py
|
|
157
|
+
# ├── submodule3/
|
|
158
|
+
# | └── c.py
|
|
159
|
+
# └── pyproject.toml
|
|
160
|
+
|
|
161
|
+
config_content = request.node.get_closest_marker("config_content")
|
|
162
|
+
if config_content:
|
|
163
|
+
(tmp_path / "pyproject.toml").write_text(config_content.args[0])
|
|
164
|
+
|
|
165
|
+
python_files = [
|
|
166
|
+
tmp_path / "submodule1" / "submodule2" / "a.py",
|
|
167
|
+
tmp_path / "submodule1" / "b.py",
|
|
168
|
+
tmp_path / "submodule3" / "c.py",
|
|
169
|
+
]
|
|
170
|
+
for file_path in python_files:
|
|
171
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
172
|
+
file_path.write_text(SRC_ORIG)
|
|
173
|
+
|
|
174
|
+
return tmp_path, python_files
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
PROJECT_CONFIG_EXCLUDE_A = "[tool.auto-walrus]\n" 'exclude = "/a"\n'
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@pytest.mark.config_content(PROJECT_CONFIG_EXCLUDE_A)
|
|
181
|
+
def test_config_file_respected(project_dir: ProjectDirT) -> None:
|
|
182
|
+
project_root, files = project_dir
|
|
183
|
+
main([str(project_root)])
|
|
184
|
+
for file in files:
|
|
185
|
+
expected = SRC_ORIG if file.name == "a.py" else SRC_CHANGED
|
|
186
|
+
assert file.read_text() == expected, f"Unexpected result for {file}"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@pytest.mark.config_content(PROJECT_CONFIG_EXCLUDE_A)
|
|
190
|
+
def test_config_file_overridden_by_cmdline(
|
|
191
|
+
project_dir: ProjectDirT,
|
|
192
|
+
) -> None:
|
|
193
|
+
project_root, files = project_dir
|
|
194
|
+
main(["--exclude", "/b", str(project_root)])
|
|
195
|
+
for file in files:
|
|
196
|
+
expected = SRC_ORIG if file.name == "b.py" else SRC_CHANGED
|
|
197
|
+
assert file.read_text() == expected, f"Unexpected result for {file}"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@pytest.mark.config_content("\n")
|
|
201
|
+
def test_config_file_no_auto_walrus(project_dir: ProjectDirT) -> None:
|
|
202
|
+
project_root, files = project_dir
|
|
203
|
+
main([str(project_root)])
|
|
204
|
+
for file in files:
|
|
205
|
+
assert file.read_text() == SRC_CHANGED, f"Unexpected result for {file}"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_config_file_missing(project_dir: ProjectDirT) -> None:
|
|
209
|
+
project_root, files = project_dir
|
|
210
|
+
main([str(project_root)])
|
|
211
|
+
for file in files:
|
|
212
|
+
assert file.read_text() == SRC_CHANGED, f"Unexpected result for {file}"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# mypy: ignore
|
|
2
|
+
# ruff: noqa
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
how = sys.argv[1]
|
|
8
|
+
|
|
9
|
+
with open("pyproject.toml", encoding="utf-8") as f:
|
|
10
|
+
content = f.read()
|
|
11
|
+
old_version = re.search(r'version = "(.*)"', content).group(1)
|
|
12
|
+
version = old_version.split(".")
|
|
13
|
+
if how == "patch":
|
|
14
|
+
version = ".".join(version[:-1] + [str(int(version[-1]) + 1)])
|
|
15
|
+
elif how == "minor":
|
|
16
|
+
version = ".".join(version[:-2] + [str(int(version[-2]) + 1), "0"])
|
|
17
|
+
elif how == "major":
|
|
18
|
+
version = ".".join([str(int(version[0]) + 1), "0", "0"])
|
|
19
|
+
content = content.replace(f'version = "{old_version}"', f'version = "{version}"')
|
|
20
|
+
with open("pyproject.toml", "w", encoding="utf-8") as f:
|
|
21
|
+
f.write(content)
|
|
22
|
+
|
|
23
|
+
subprocess.run(["git", "commit", "-a", "-m", f"Bump version to {version}"])
|
|
24
|
+
subprocess.run(["git", "tag", "-a", version, "-m", version])
|
|
25
|
+
subprocess.run(["git", "push", "--follow-tags"])
|
|
26
|
+
|
auto_walrus-0.2.2/PKG-INFO
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: auto_walrus
|
|
3
|
-
Version: 0.2.2
|
|
4
|
-
Summary: Automatically apply the awesome walrus operator
|
|
5
|
-
Home-page: https://github.com/MarcoGorelli/auto-walrus
|
|
6
|
-
Author: Marco Gorelli
|
|
7
|
-
License: MIT
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
13
|
-
Requires-Python: >=3.8
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
|
|
17
|
-
<h1 align="center">
|
|
18
|
-
auto-walrus
|
|
19
|
-
</h1>
|
|
20
|
-
|
|
21
|
-
<p align="center">
|
|
22
|
-
<img width="458" alt="auto-walrus" src="https://user-images.githubusercontent.com/33491632/195613331-f7442140-09da-4376-90aa-2ac4aaa242fa.png">
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
|
-
auto-walrus
|
|
26
|
-
===========
|
|
27
|
-
[](https://github.com/MarcoGorelli/auto-walrus/actions?workflow=tox)
|
|
28
|
-
[](https://codecov.io/gh/MarcoGorelli/auto-walrus)
|
|
29
|
-
[](https://results.pre-commit.ci/latest/github/MarcoGorelli/auto-walrus/main)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
A tool and pre-commit hook to automatically apply the awesome walrus operator.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## Installation
|
|
36
|
-
|
|
37
|
-
```console
|
|
38
|
-
pip install auto-walrus
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Usage as a pre-commit hook
|
|
42
|
-
|
|
43
|
-
See [pre-commit](https://github.com/pre-commit/pre-commit) for instructions
|
|
44
|
-
|
|
45
|
-
Sample `.pre-commit-config.yaml`:
|
|
46
|
-
|
|
47
|
-
```yaml
|
|
48
|
-
- repo: https://github.com/MarcoGorelli/auto-walrus
|
|
49
|
-
rev: v0.2.2
|
|
50
|
-
hooks:
|
|
51
|
-
- id: auto-walrus
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Command-line example
|
|
55
|
-
|
|
56
|
-
```console
|
|
57
|
-
auto-walrus myfile.py
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
```diff
|
|
61
|
-
- n = 10
|
|
62
|
-
- if n > 3:
|
|
63
|
-
+ if (n := 10) > 3:
|
|
64
|
-
print(n)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Configuration
|
|
68
|
-
|
|
69
|
-
Using the walrus operator can result in longer lines. Lines longer than what you
|
|
70
|
-
pass to ``--line-length`` won't be rewritten to use walrus operators.
|
|
71
|
-
|
|
72
|
-
E.g.
|
|
73
|
-
```
|
|
74
|
-
auto-walrus myfile_1.py myfile_2.py --line-length 89
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Lines with comments won't be rewritten.
|
|
78
|
-
|
|
79
|
-
## Used by
|
|
80
|
-
|
|
81
|
-
To my great surprise, this is being used by:
|
|
82
|
-
|
|
83
|
-
- https://github.com/python-graphblas/python-graphblas
|
|
84
|
-
- https://github.com/Remi-Gau/bids2cite
|
|
85
|
-
- https://github.com/TheAlgorithms/Python
|
|
86
|
-
|
|
87
|
-
Anyone else? Please let me know, or you can open a pull request to add yourself.
|
|
88
|
-
|
|
89
|
-
## Testimonials
|
|
90
|
-
|
|
91
|
-
**Christopher Redwine**, [Senior Software Engineer at TechnologyAdvice](https://github.com/chrisRedwine)
|
|
92
|
-
|
|
93
|
-
> hmm, i dunno about this one chief
|
|
94
|
-
|
|
95
|
-
**Michael Kennedy & Brian Okken**, [hosts of the Python Bytes podcast](https://pythonbytes.fm/):
|
|
96
|
-
|
|
97
|
-
> I kind of like this being separate from other tools
|
|
98
|
-
|
|
99
|
-
**Someone on Discord**
|
|
100
|
-
|
|
101
|
-
> you're a monster
|
|
102
|
-
|
|
103
|
-
**Will McGugan**, [CEO / Founder of http://Textualize.io](https://www.willmcgugan.com/):
|
|
104
|
-
|
|
105
|
-
> Embrace the Walrus!
|
|
106
|
-
|
|
107
|
-
## Credits
|
|
108
|
-
|
|
109
|
-
Logo by [lion_space](https://www.fiverr.com/lion_space)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
auto_walrus
|
auto_walrus-0.2.2/setup.cfg
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
[metadata]
|
|
2
|
-
name = auto_walrus
|
|
3
|
-
version = 0.2.2
|
|
4
|
-
description = Automatically apply the awesome walrus operator
|
|
5
|
-
long_description = file: README.md
|
|
6
|
-
long_description_content_type = text/markdown
|
|
7
|
-
url = https://github.com/MarcoGorelli/auto-walrus
|
|
8
|
-
author = Marco Gorelli
|
|
9
|
-
license = MIT
|
|
10
|
-
license_file = LICENSE
|
|
11
|
-
classifiers =
|
|
12
|
-
License :: OSI Approved :: MIT License
|
|
13
|
-
Programming Language :: Python :: 3
|
|
14
|
-
Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Programming Language :: Python :: Implementation :: CPython
|
|
16
|
-
Programming Language :: Python :: Implementation :: PyPy
|
|
17
|
-
|
|
18
|
-
[options]
|
|
19
|
-
py_modules = auto_walrus
|
|
20
|
-
python_requires = >=3.8
|
|
21
|
-
|
|
22
|
-
[options.entry_points]
|
|
23
|
-
console_scripts =
|
|
24
|
-
auto-walrus = auto_walrus:main
|
|
25
|
-
|
|
26
|
-
[bdist_wheel]
|
|
27
|
-
universal = True
|
|
28
|
-
|
|
29
|
-
[coverage:run]
|
|
30
|
-
plugins = covdefaults
|
|
31
|
-
|
|
32
|
-
[mypy]
|
|
33
|
-
check_untyped_defs = true
|
|
34
|
-
disallow_any_generics = true
|
|
35
|
-
disallow_incomplete_defs = true
|
|
36
|
-
disallow_untyped_defs = true
|
|
37
|
-
no_implicit_optional = true
|
|
38
|
-
|
|
39
|
-
[mypy-testing.*]
|
|
40
|
-
disallow_untyped_defs = false
|
|
41
|
-
|
|
42
|
-
[mypy-tests.*]
|
|
43
|
-
disallow_untyped_defs = false
|
|
44
|
-
|
|
45
|
-
[egg_info]
|
|
46
|
-
tag_build =
|
|
47
|
-
tag_date = 0
|
|
48
|
-
|
auto_walrus-0.2.2/setup.py
DELETED
|
File without changes
|