zpretty 3.1.1__py3-none-any.whl
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.
- zpretty/__init__.py +0 -0
- zpretty/attributes.py +261 -0
- zpretty/cli.py +247 -0
- zpretty/elements.py +401 -0
- zpretty/prettifier.py +146 -0
- zpretty/tests/__init__.py +0 -0
- zpretty/tests/broken/broken.xml +2 -0
- zpretty/tests/include-exclude/.venv/bar.html +0 -0
- zpretty/tests/include-exclude/.venv/bar.pt +0 -0
- zpretty/tests/include-exclude/.venv/bar.txt +0 -0
- zpretty/tests/include-exclude/.venv/bar.xml +0 -0
- zpretty/tests/include-exclude/.venv/bar.zcml +0 -0
- zpretty/tests/include-exclude/.venv/foo.html +0 -0
- zpretty/tests/include-exclude/.venv/foo.pt +0 -0
- zpretty/tests/include-exclude/.venv/foo.txt +0 -0
- zpretty/tests/include-exclude/.venv/foo.xml +0 -0
- zpretty/tests/include-exclude/.venv/foo.zcml +0 -0
- zpretty/tests/include-exclude/foo/bar/bar.html +0 -0
- zpretty/tests/include-exclude/foo/bar/bar.pt +0 -0
- zpretty/tests/include-exclude/foo/bar/bar.txt +0 -0
- zpretty/tests/include-exclude/foo/bar/bar.xml +0 -0
- zpretty/tests/include-exclude/foo/bar/bar.zcml +0 -0
- zpretty/tests/include-exclude/foo/bar/foo.html +0 -0
- zpretty/tests/include-exclude/foo/bar/foo.pt +0 -0
- zpretty/tests/include-exclude/foo/bar/foo.txt +0 -0
- zpretty/tests/include-exclude/foo/bar/foo.xml +0 -0
- zpretty/tests/include-exclude/foo/bar/foo.zcml +0 -0
- zpretty/tests/include-exclude/venv/bar.html +0 -0
- zpretty/tests/include-exclude/venv/bar.pt +0 -0
- zpretty/tests/include-exclude/venv/bar.txt +0 -0
- zpretty/tests/include-exclude/venv/bar.xml +0 -0
- zpretty/tests/include-exclude/venv/bar.zcml +0 -0
- zpretty/tests/include-exclude/venv/foo.html +0 -0
- zpretty/tests/include-exclude/venv/foo.pt +0 -0
- zpretty/tests/include-exclude/venv/foo.txt +0 -0
- zpretty/tests/include-exclude/venv/foo.xml +0 -0
- zpretty/tests/include-exclude/venv/foo.zcml +0 -0
- zpretty/tests/mock.py +7 -0
- zpretty/tests/original/sample.txt +1 -0
- zpretty/tests/original/sample.zcml +66 -0
- zpretty/tests/original/sample_dtml.dtml +10 -0
- zpretty/tests/original/sample_html.html +102 -0
- zpretty/tests/original/sample_html4.html +102 -0
- zpretty/tests/original/sample_html_with_preprocessing_instruction.html +17 -0
- zpretty/tests/original/sample_pt.pt +52 -0
- zpretty/tests/original/sample_xml.xml +39 -0
- zpretty/tests/original/text_with_markup.md +12 -0
- zpretty/tests/test_attributes.py +79 -0
- zpretty/tests/test_cli.py +221 -0
- zpretty/tests/test_elements.py +110 -0
- zpretty/tests/test_functions.py +27 -0
- zpretty/tests/test_readme.py +59 -0
- zpretty/tests/test_xml.py +53 -0
- zpretty/tests/test_zcml.py +244 -0
- zpretty/tests/test_zpretty.py +221 -0
- zpretty/text.py +40 -0
- zpretty/xml.py +90 -0
- zpretty/zcml.py +580 -0
- zpretty-3.1.1.dist-info/METADATA +386 -0
- zpretty-3.1.1.dist-info/RECORD +64 -0
- zpretty-3.1.1.dist-info/WHEEL +5 -0
- zpretty-3.1.1.dist-info/entry_points.txt +2 -0
- zpretty-3.1.1.dist-info/licenses/LICENSE +24 -0
- zpretty-3.1.1.dist-info/top_level.txt +1 -0
zpretty/__init__.py
ADDED
|
File without changes
|
zpretty/attributes.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from html import escape
|
|
6
|
+
except ImportError: # pragma: no cover
|
|
7
|
+
# Python < 3.8
|
|
8
|
+
from cgi import escape
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PrettyAttributes(object):
|
|
15
|
+
"""Render attributes in a pretty way.
|
|
16
|
+
|
|
17
|
+
- one per line
|
|
18
|
+
- sorted semantically and alphabetically
|
|
19
|
+
- with properly indented and escaped values
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_attribute_template = "{name}={quote}{value}{quote}"
|
|
23
|
+
_boolean_attributes_are_allowed = True
|
|
24
|
+
|
|
25
|
+
_known_boolean_attributes = (
|
|
26
|
+
"allowfullscreen",
|
|
27
|
+
"allowpaymentrequest",
|
|
28
|
+
"async",
|
|
29
|
+
"autofocus",
|
|
30
|
+
"autoplay",
|
|
31
|
+
"checked",
|
|
32
|
+
"controls",
|
|
33
|
+
"default",
|
|
34
|
+
"disabled",
|
|
35
|
+
"formnovalidate",
|
|
36
|
+
"hidden",
|
|
37
|
+
"ismap",
|
|
38
|
+
"itemscope",
|
|
39
|
+
"loop",
|
|
40
|
+
"multiple",
|
|
41
|
+
"muted",
|
|
42
|
+
"nomodule",
|
|
43
|
+
"novalidate",
|
|
44
|
+
"open",
|
|
45
|
+
"playsinline",
|
|
46
|
+
"readonly",
|
|
47
|
+
"required",
|
|
48
|
+
"reversed",
|
|
49
|
+
"selected",
|
|
50
|
+
"truespeed",
|
|
51
|
+
)
|
|
52
|
+
_multiline_prefix = " "
|
|
53
|
+
_multiline_attributes = ()
|
|
54
|
+
_tal_multiline_attributes = (
|
|
55
|
+
"attributes",
|
|
56
|
+
"define",
|
|
57
|
+
"tal:attributes",
|
|
58
|
+
"tal:define",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
_tal_attribute_order = (
|
|
62
|
+
"tal:define",
|
|
63
|
+
"tal:switch",
|
|
64
|
+
"tal:condition",
|
|
65
|
+
"tal:repeat",
|
|
66
|
+
"tal:case",
|
|
67
|
+
"tal:content",
|
|
68
|
+
"tal:replace",
|
|
69
|
+
"tal:omit-tag",
|
|
70
|
+
"tal:attributes",
|
|
71
|
+
"tal:on-error",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
_i18n_attributes = (
|
|
75
|
+
"i18n:translate",
|
|
76
|
+
"i18n:domain",
|
|
77
|
+
"i18n:context",
|
|
78
|
+
"i18n:source",
|
|
79
|
+
"i18n:target",
|
|
80
|
+
"i18n:name",
|
|
81
|
+
"i18n:attributes",
|
|
82
|
+
"i18n:data",
|
|
83
|
+
"i18n:comment",
|
|
84
|
+
"i18n:ignore",
|
|
85
|
+
"i18n:ignore-attributes",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def __init__(self, attributes, element=None):
|
|
89
|
+
"""attributes is a dict like object"""
|
|
90
|
+
self.attributes = attributes
|
|
91
|
+
self.element = element
|
|
92
|
+
|
|
93
|
+
def __len__(self):
|
|
94
|
+
return len(self.attributes)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def prefix(self):
|
|
98
|
+
"""Return the prefix for the attributes
|
|
99
|
+
|
|
100
|
+
The returned value will be a number of spaces equal to the tag name length + 2,
|
|
101
|
+
e.g., in this case it will be 8
|
|
102
|
+
(6 for foobar + 2 (the leading < and the space after foobar)):
|
|
103
|
+
<foobar foo="1"
|
|
104
|
+
________bar="2"
|
|
105
|
+
/>
|
|
106
|
+
"""
|
|
107
|
+
if not self.element:
|
|
108
|
+
return " "
|
|
109
|
+
return " " * (len(self.element.tag or "") + 2)
|
|
110
|
+
|
|
111
|
+
def sort_attributes(self, name):
|
|
112
|
+
"""This sorts the attribute trying to group them semantically
|
|
113
|
+
|
|
114
|
+
Starting from the top:
|
|
115
|
+
|
|
116
|
+
1. xml namespaces
|
|
117
|
+
2. class, id
|
|
118
|
+
3. attributes not belonging in to other categories (default)
|
|
119
|
+
4. data- attributes
|
|
120
|
+
5. tal attributes
|
|
121
|
+
6. i18n attributes
|
|
122
|
+
"""
|
|
123
|
+
if name.startswith("xmlns"):
|
|
124
|
+
return (0, name)
|
|
125
|
+
if name in ("class", "id"):
|
|
126
|
+
return (100, name)
|
|
127
|
+
if name.startswith("data"):
|
|
128
|
+
return (300, name)
|
|
129
|
+
if "tal:" + name in self._tal_attribute_order:
|
|
130
|
+
tal_index = self._tal_attribute_order.index("tal:" + name)
|
|
131
|
+
return (400 + tal_index, name)
|
|
132
|
+
if name in self._tal_attribute_order:
|
|
133
|
+
tal_index = self._tal_attribute_order.index(name)
|
|
134
|
+
return (400 + tal_index, name)
|
|
135
|
+
if name in self._i18n_attributes:
|
|
136
|
+
return (900, name)
|
|
137
|
+
return (200, name)
|
|
138
|
+
|
|
139
|
+
def format_multiline(self, name, value):
|
|
140
|
+
""""""
|
|
141
|
+
value_lines = filter(None, value.split())
|
|
142
|
+
line_joiner = "\n" + (" " * (len(name) + 2))
|
|
143
|
+
return line_joiner.join(value_lines)
|
|
144
|
+
|
|
145
|
+
def format_tal_multiline(self, value):
|
|
146
|
+
"""There are some tal specific attributes that contain ; separated
|
|
147
|
+
statements.
|
|
148
|
+
They are used to define variables or set other attributes.
|
|
149
|
+
You can define many variables by adding statements separated by ';'.
|
|
150
|
+
If the statement contains a ';', it will be escaped as ';;'.
|
|
151
|
+
|
|
152
|
+
It is convenient to always have those attribute in the form:
|
|
153
|
+
|
|
154
|
+
tal:define="
|
|
155
|
+
var1 statement1;
|
|
156
|
+
var2 statement2;
|
|
157
|
+
...
|
|
158
|
+
"
|
|
159
|
+
"""
|
|
160
|
+
# temp skip ';;' the escape sequence to enter a ';' in a statement
|
|
161
|
+
statements = value.replace(";;", "<>").split(";")
|
|
162
|
+
|
|
163
|
+
# We always want an empty line first...
|
|
164
|
+
lines = [""]
|
|
165
|
+
try:
|
|
166
|
+
line_prefix = self.element.prefix + self.prefix + self._multiline_prefix
|
|
167
|
+
except AttributeError:
|
|
168
|
+
line_prefix = self._multiline_prefix
|
|
169
|
+
|
|
170
|
+
for statement in statements:
|
|
171
|
+
statement = statement.strip()
|
|
172
|
+
if statement:
|
|
173
|
+
if not statement.endswith(";"):
|
|
174
|
+
statement += ";"
|
|
175
|
+
lines.append(line_prefix + statement)
|
|
176
|
+
# ... and at the end dedent
|
|
177
|
+
lines.append(line_prefix[:-2])
|
|
178
|
+
|
|
179
|
+
new_value = "\n".join(lines)
|
|
180
|
+
# restore ';;'
|
|
181
|
+
return new_value.replace("<>", ";;")
|
|
182
|
+
|
|
183
|
+
def is_tal_attribute(self, name):
|
|
184
|
+
"""Check if the attribute is a tal attribute"""
|
|
185
|
+
if name.startswith("tal:"):
|
|
186
|
+
return True
|
|
187
|
+
try:
|
|
188
|
+
if not self.element.context.name.startswith("tal:"):
|
|
189
|
+
return False
|
|
190
|
+
except AttributeError:
|
|
191
|
+
return False
|
|
192
|
+
if f"tal:{name}" in self._tal_attribute_order:
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
def maybe_escape(self, name, value):
|
|
196
|
+
"""Escape the value if needed"""
|
|
197
|
+
if self.is_tal_attribute(name):
|
|
198
|
+
# Never escape what we have in tal attributes
|
|
199
|
+
return value
|
|
200
|
+
|
|
201
|
+
return escape(value, quote=False)
|
|
202
|
+
|
|
203
|
+
def can_be_valueless(self, name):
|
|
204
|
+
"""Check if the attribute name can be without a value"""
|
|
205
|
+
if not self._boolean_attributes_are_allowed:
|
|
206
|
+
return False
|
|
207
|
+
if name.startswith("data-"):
|
|
208
|
+
return True
|
|
209
|
+
if name in self._known_boolean_attributes:
|
|
210
|
+
return True
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
def lines(self):
|
|
214
|
+
"""Take the attributes, sort them and prettify their values"""
|
|
215
|
+
attributes = self.attributes
|
|
216
|
+
sorted_names = sorted(attributes, key=self.sort_attributes)
|
|
217
|
+
lines = []
|
|
218
|
+
for name in sorted_names:
|
|
219
|
+
value = attributes[name]
|
|
220
|
+
if isinstance(value, list):
|
|
221
|
+
# Happens, e.g., for the class attribute
|
|
222
|
+
value = " ".join(value)
|
|
223
|
+
if name in self._multiline_attributes:
|
|
224
|
+
value = self.format_multiline(name, value)
|
|
225
|
+
elif name in self._tal_multiline_attributes:
|
|
226
|
+
value = self.format_tal_multiline(value)
|
|
227
|
+
if not value and self.can_be_valueless(name):
|
|
228
|
+
line = name
|
|
229
|
+
else:
|
|
230
|
+
if '"' in value:
|
|
231
|
+
quote = "'"
|
|
232
|
+
else:
|
|
233
|
+
quote = '"'
|
|
234
|
+
|
|
235
|
+
line = self._attribute_template.format(
|
|
236
|
+
name=name, quote=quote, value=self.maybe_escape(name, value)
|
|
237
|
+
)
|
|
238
|
+
lines.append(line)
|
|
239
|
+
return lines
|
|
240
|
+
|
|
241
|
+
def lstrip(self):
|
|
242
|
+
"""This returns the attributes with the left spaces removed"""
|
|
243
|
+
return self().lstrip()
|
|
244
|
+
|
|
245
|
+
def __call__(self):
|
|
246
|
+
"""Render the attributes as text
|
|
247
|
+
|
|
248
|
+
Render and an empty string if no attributes
|
|
249
|
+
If we have one attribute we do not indent it
|
|
250
|
+
If we have many we indent them
|
|
251
|
+
"""
|
|
252
|
+
if len(self) == 0:
|
|
253
|
+
return ""
|
|
254
|
+
if len(self) == 1:
|
|
255
|
+
for line in self.lines():
|
|
256
|
+
return line
|
|
257
|
+
if self.element:
|
|
258
|
+
prefix = self.element.prefix + self.prefix
|
|
259
|
+
else:
|
|
260
|
+
prefix = ""
|
|
261
|
+
return prefix + f"\n{prefix}".join(self.lines())
|
zpretty/cli.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
from argparse import ArgumentParser
|
|
2
|
+
from os.path import splitext
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from sys import stderr
|
|
5
|
+
from sys import stdout
|
|
6
|
+
from zpretty.prettifier import ZPrettifier
|
|
7
|
+
from zpretty.xml import XMLPrettifier
|
|
8
|
+
from zpretty.zcml import ZCMLPrettifier
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
# Python >= 3.8
|
|
15
|
+
from importlib.metadata import version
|
|
16
|
+
|
|
17
|
+
version = version("zpretty")
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Python < 3.8
|
|
20
|
+
from pkg_resources import get_distribution
|
|
21
|
+
|
|
22
|
+
version = get_distribution("zpretty").version
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CLIRunner:
|
|
26
|
+
"""A class to run zpretty from the command line"""
|
|
27
|
+
|
|
28
|
+
_default_include = r"\.(html|pt|xml|zcml)$"
|
|
29
|
+
_default_exclude = (
|
|
30
|
+
r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|venv|"
|
|
31
|
+
r"\.svn|\.ipynb_checkpoints|_build|buck-out|build|dist|__pypackages__)/"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.errors = []
|
|
36
|
+
self.config = self.parser.parse_args()
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def parser(self):
|
|
40
|
+
"""The parser we are using to parse the command line arguments"""
|
|
41
|
+
parser = ArgumentParser(
|
|
42
|
+
prog="zpretty",
|
|
43
|
+
description="An opinionated HTML/XML soup formatter",
|
|
44
|
+
epilog=f"The default exclude pattern is: `{self._default_exclude}`",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--encoding",
|
|
48
|
+
help="The file encoding (defaults to utf8)",
|
|
49
|
+
action="store",
|
|
50
|
+
dest="encoding",
|
|
51
|
+
default="utf8",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"-i",
|
|
55
|
+
"--inplace",
|
|
56
|
+
help="Format files in place (overwrite existing file)",
|
|
57
|
+
action="store_true",
|
|
58
|
+
dest="inplace",
|
|
59
|
+
default=False,
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"-v",
|
|
63
|
+
"--version",
|
|
64
|
+
help="Show zpretty version number",
|
|
65
|
+
action="version",
|
|
66
|
+
version=f"zpretty {version}",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"-x",
|
|
70
|
+
"--xml",
|
|
71
|
+
help="Treat the input file(s) as XML",
|
|
72
|
+
action="store_true",
|
|
73
|
+
dest="xml",
|
|
74
|
+
default=False,
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"-z",
|
|
78
|
+
"--zcml",
|
|
79
|
+
help="Treat the input file(s) as XML. Follow the ZCML styleguide",
|
|
80
|
+
action="store_true",
|
|
81
|
+
dest="zcml",
|
|
82
|
+
default=False,
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--check",
|
|
86
|
+
help=(
|
|
87
|
+
"Return code 0 if nothing would be changed, "
|
|
88
|
+
"1 if some files would be reformatted"
|
|
89
|
+
),
|
|
90
|
+
action="store_true",
|
|
91
|
+
dest="check",
|
|
92
|
+
default=False,
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--include",
|
|
96
|
+
help=(
|
|
97
|
+
f"A regular expression that matches files and "
|
|
98
|
+
f" directories that should be included on recursive searches. "
|
|
99
|
+
f"An empty value means all files are included regardless of the name. "
|
|
100
|
+
f"Use forward slashes for directories on all platforms (Windows, too). "
|
|
101
|
+
f"Exclusions are calculated first, inclusions later. "
|
|
102
|
+
f"[default: {self._default_include}]"
|
|
103
|
+
),
|
|
104
|
+
action="store",
|
|
105
|
+
dest="include",
|
|
106
|
+
default=self._default_include,
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--exclude",
|
|
110
|
+
help=(
|
|
111
|
+
f"A regular expression that matches files and "
|
|
112
|
+
f"directories that should be excluded on "
|
|
113
|
+
f"recursive searches. An empty value means no "
|
|
114
|
+
f"paths are excluded. Use forward slashes for "
|
|
115
|
+
f"directories on all platforms (Windows, too). "
|
|
116
|
+
f"Exclusions are calculated first, inclusions "
|
|
117
|
+
f"later. [default: {self._default_exclude}] "
|
|
118
|
+
),
|
|
119
|
+
action="store",
|
|
120
|
+
dest="exclude",
|
|
121
|
+
default=self._default_exclude,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--extend-exclude",
|
|
126
|
+
help=(
|
|
127
|
+
"Like --exclude, but adds additional files "
|
|
128
|
+
"and directories on top of the excluded ones. "
|
|
129
|
+
"(Useful if you simply want to add to the default)"
|
|
130
|
+
),
|
|
131
|
+
action="store",
|
|
132
|
+
dest="extend_exclude",
|
|
133
|
+
default=None,
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"paths",
|
|
137
|
+
nargs="*",
|
|
138
|
+
default="-",
|
|
139
|
+
help="The list of files or directory to prettify (defaults to stdin). "
|
|
140
|
+
"If a directory is passed, all files and directories matching the regular "
|
|
141
|
+
"expression passed to --include will be prettified.",
|
|
142
|
+
)
|
|
143
|
+
return parser
|
|
144
|
+
|
|
145
|
+
def choose_prettifier(self, path):
|
|
146
|
+
"""Choose the best prettifier given the config and the input file"""
|
|
147
|
+
config = self.config
|
|
148
|
+
if config.zcml:
|
|
149
|
+
return ZCMLPrettifier
|
|
150
|
+
if config.xml:
|
|
151
|
+
return XMLPrettifier
|
|
152
|
+
ext = splitext(path)[-1].lower()
|
|
153
|
+
if ext == ".xml":
|
|
154
|
+
return XMLPrettifier
|
|
155
|
+
if ext == ".zcml":
|
|
156
|
+
return ZCMLPrettifier
|
|
157
|
+
return ZPrettifier
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def good_paths(self):
|
|
161
|
+
"""Return a list of good paths"""
|
|
162
|
+
good_paths = []
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
exclude = re.compile(self.config.exclude)
|
|
166
|
+
except re.error:
|
|
167
|
+
exclude = re.compile(self._default_exclude)
|
|
168
|
+
self.errors.append(
|
|
169
|
+
f"Invalid regular expression for --exclude: {self.config.exclude!r}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
extend_exclude = self.config.extend_exclude and re.compile(
|
|
174
|
+
self.config.extend_exclude
|
|
175
|
+
)
|
|
176
|
+
except re.error:
|
|
177
|
+
extend_exclude = None
|
|
178
|
+
self.errors.append(
|
|
179
|
+
f"Invalid regular expression for --extend-exclude: "
|
|
180
|
+
f"{self.config.extend_exclude!r}"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
include = re.compile(self.config.include)
|
|
185
|
+
except re.error:
|
|
186
|
+
include = re.compile(self._default_include)
|
|
187
|
+
self.errors.append(
|
|
188
|
+
f"Invalid regular expression for --include: {self.config.include!r}"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
for path in self.config.paths:
|
|
192
|
+
# use Pathlib to check if the file exists and it is a file
|
|
193
|
+
if path == "-":
|
|
194
|
+
good_paths.append(path)
|
|
195
|
+
continue
|
|
196
|
+
if exclude.match(path) or (extend_exclude and extend_exclude.match(path)):
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
path_instance = Path(path)
|
|
200
|
+
if path_instance.is_file():
|
|
201
|
+
good_paths.append(path)
|
|
202
|
+
elif path_instance.is_dir():
|
|
203
|
+
for file in path_instance.glob("**/*"):
|
|
204
|
+
if file.is_file():
|
|
205
|
+
if (
|
|
206
|
+
include.search(str(file))
|
|
207
|
+
and not exclude.search(str(file))
|
|
208
|
+
and not (
|
|
209
|
+
extend_exclude and extend_exclude.search(str(file))
|
|
210
|
+
)
|
|
211
|
+
):
|
|
212
|
+
good_paths.append(str(file))
|
|
213
|
+
else:
|
|
214
|
+
self.errors.append(f"Cannot open: {path}")
|
|
215
|
+
|
|
216
|
+
return sorted(good_paths)
|
|
217
|
+
|
|
218
|
+
def run(self):
|
|
219
|
+
"""Prettify each filename passed in the command line"""
|
|
220
|
+
encoding = self.config.encoding
|
|
221
|
+
for path in self.good_paths:
|
|
222
|
+
# use Pathlib to check if the file exists and it is a file
|
|
223
|
+
Prettifier = self.choose_prettifier(path)
|
|
224
|
+
prettifier = Prettifier(path, encoding=encoding)
|
|
225
|
+
if self.config.check:
|
|
226
|
+
if not prettifier.check():
|
|
227
|
+
self.errors.append(f"This file would be rewritten: {path}")
|
|
228
|
+
continue
|
|
229
|
+
prettified = prettifier()
|
|
230
|
+
if self.config.inplace and not path == "-":
|
|
231
|
+
with open(path, "w") as f:
|
|
232
|
+
f.write(prettified)
|
|
233
|
+
continue
|
|
234
|
+
stdout.write(prettified)
|
|
235
|
+
|
|
236
|
+
if self.errors:
|
|
237
|
+
message = "\n".join(self.errors)
|
|
238
|
+
stderr.write(f"{message}\n")
|
|
239
|
+
exit(1)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def run():
|
|
243
|
+
CLIRunner().run() # pragma: no cover
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
if __name__ == "__main__":
|
|
247
|
+
run() # pragma: no cover
|