check-empty 2.0.0__tar.gz → 3.0.1__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.
- {check_empty-2.0.0 → check_empty-3.0.1}/PKG-INFO +9 -8
- {check_empty-2.0.0 → check_empty-3.0.1}/README.md +7 -6
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/__init__.py +37 -44
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/__main__.py +9 -8
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/archives.py +18 -13
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/constants.py +5 -5
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/reporter/__init__.py +11 -9
- check_empty-2.0.0/check_empty/reporter/abc.py → check_empty-3.0.1/check_empty/reporter/abcdef.py +8 -6
- check_empty-2.0.0/check_empty/reporter/dict.py → check_empty-3.0.1/check_empty/reporter/dct.py +7 -5
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/reporter/exceptions.py +14 -10
- {check_empty-2.0.0 → check_empty-3.0.1}/pyproject.toml +12 -16
- {check_empty-2.0.0 → check_empty-3.0.1}/pyproject.toml.orig +12 -11
- {check_empty-2.0.0 → check_empty-3.0.1}/AUTHORS.md +0 -0
- {check_empty-2.0.0 → check_empty-3.0.1}/LICENSE +0 -0
- {check_empty-2.0.0 → check_empty-3.0.1}/check_empty/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: check-empty
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: Command-line utility, as well as a hook usable by the Python pre-commit framework, and a GitHub Action, to ensure that selected files are empty.
|
|
5
5
|
Keywords: check,command-line,empty,files,github-action,helper,hook,lightweight,pre-commit,prek,tool,utility
|
|
6
6
|
Author: Jonathan Dung
|
|
@@ -27,7 +27,7 @@ Classifier: Topic :: Utilities
|
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
28
|
Requires-Dist: py7zr==1.1.3 ; extra == '7z'
|
|
29
29
|
Requires-Dist: acefile==0.6.14 ; extra == 'ace'
|
|
30
|
-
Requires-Dist: check-empty[7z,ace,ar,lzh,rar]==
|
|
30
|
+
Requires-Dist: check-empty[7z,ace,ar,lzh,rar]==3.0.1 ; extra == 'all'
|
|
31
31
|
Requires-Dist: arpy==2.4.0 ; extra == 'ar'
|
|
32
32
|
Requires-Dist: lhafile==0.3.1 ; extra == 'lzh'
|
|
33
33
|
Requires-Dist: rarfile==4.5 ; extra == 'rar'
|
|
@@ -42,7 +42,7 @@ Provides-Extra: lzh
|
|
|
42
42
|
Provides-Extra: rar
|
|
43
43
|
Description-Content-Type: text/markdown
|
|
44
44
|
|
|
45
|
-
# [check-empty](https://pypi.org/
|
|
45
|
+
# [check-empty](https://pypi.org/project/check-empty/3.0.1)
|
|
46
46
|
|
|
47
47
|
[](https://github.com/TheBSD/StandWithPalestine/blob/main/docs/README.md)
|
|
48
48
|
|
|
@@ -57,7 +57,8 @@ specified.
|
|
|
57
57
|
|
|
58
58
|
## Prerequisites
|
|
59
59
|
|
|
60
|
-
Supports every Python 3.10 runtime. There are no other requirements
|
|
60
|
+
Supports every Python 3.10 runtime. There are no other requirements unless you want to
|
|
61
|
+
check if files in certain types of (compressed) archives are empty.
|
|
61
62
|
|
|
62
63
|
## Quickstart
|
|
63
64
|
|
|
@@ -125,7 +126,7 @@ As a pre-commit hook:
|
|
|
125
126
|
# .pre-commit-config.yaml
|
|
126
127
|
repos:
|
|
127
128
|
- repo: https://github.com/jonathandung/check-empty
|
|
128
|
-
rev:
|
|
129
|
+
rev: v3.0.1 # repository version
|
|
129
130
|
hooks:
|
|
130
131
|
- id: check-empty # the hook
|
|
131
132
|
args: # example list of arguments
|
|
@@ -141,7 +142,7 @@ equivalent in `prek.toml` format:
|
|
|
141
142
|
```toml
|
|
142
143
|
[[repos]]
|
|
143
144
|
repo = "https://github.com/jonathandung/check-empty"
|
|
144
|
-
rev = "
|
|
145
|
+
rev = "v3.0.1"
|
|
145
146
|
|
|
146
147
|
[[repos.hooks]]
|
|
147
148
|
id = "check-empty"
|
|
@@ -165,7 +166,7 @@ or (TOML 1.1+):
|
|
|
165
166
|
# using multiline inline tables
|
|
166
167
|
[[repos]]
|
|
167
168
|
repo = "https://github.com/jonathandung/check-empty"
|
|
168
|
-
rev = "
|
|
169
|
+
rev = "v3.0.1"
|
|
169
170
|
hooks = [{
|
|
170
171
|
id = "check-empty",
|
|
171
172
|
args = ["-Q"],
|
|
@@ -185,7 +186,7 @@ As a GitHub Actions workflow step:
|
|
|
185
186
|
|
|
186
187
|
```yaml
|
|
187
188
|
steps:
|
|
188
|
-
- uses: jonathandung/check-empty@
|
|
189
|
+
- uses: jonathandung/check-empty@v3.0.1 # the latest version on the GitHub Actions
|
|
189
190
|
# marketplace; this step will fail and subsequent jobs will not run if any file is
|
|
190
191
|
# not empty
|
|
191
192
|
with:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# [check-empty](https://pypi.org/
|
|
1
|
+
# [check-empty](https://pypi.org/project/check-empty/3.0.1)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/TheBSD/StandWithPalestine/blob/main/docs/README.md)
|
|
4
4
|
|
|
@@ -13,7 +13,8 @@ specified.
|
|
|
13
13
|
|
|
14
14
|
## Prerequisites
|
|
15
15
|
|
|
16
|
-
Supports every Python 3.10 runtime. There are no other requirements
|
|
16
|
+
Supports every Python 3.10 runtime. There are no other requirements unless you want to
|
|
17
|
+
check if files in certain types of (compressed) archives are empty.
|
|
17
18
|
|
|
18
19
|
## Quickstart
|
|
19
20
|
|
|
@@ -81,7 +82,7 @@ As a pre-commit hook:
|
|
|
81
82
|
# .pre-commit-config.yaml
|
|
82
83
|
repos:
|
|
83
84
|
- repo: https://github.com/jonathandung/check-empty
|
|
84
|
-
rev:
|
|
85
|
+
rev: v3.0.1 # repository version
|
|
85
86
|
hooks:
|
|
86
87
|
- id: check-empty # the hook
|
|
87
88
|
args: # example list of arguments
|
|
@@ -97,7 +98,7 @@ equivalent in `prek.toml` format:
|
|
|
97
98
|
```toml
|
|
98
99
|
[[repos]]
|
|
99
100
|
repo = "https://github.com/jonathandung/check-empty"
|
|
100
|
-
rev = "
|
|
101
|
+
rev = "v3.0.1"
|
|
101
102
|
|
|
102
103
|
[[repos.hooks]]
|
|
103
104
|
id = "check-empty"
|
|
@@ -121,7 +122,7 @@ or (TOML 1.1+):
|
|
|
121
122
|
# using multiline inline tables
|
|
122
123
|
[[repos]]
|
|
123
124
|
repo = "https://github.com/jonathandung/check-empty"
|
|
124
|
-
rev = "
|
|
125
|
+
rev = "v3.0.1"
|
|
125
126
|
hooks = [{
|
|
126
127
|
id = "check-empty",
|
|
127
128
|
args = ["-Q"],
|
|
@@ -141,7 +142,7 @@ As a GitHub Actions workflow step:
|
|
|
141
142
|
|
|
142
143
|
```yaml
|
|
143
144
|
steps:
|
|
144
|
-
- uses: jonathandung/check-empty@
|
|
145
|
+
- uses: jonathandung/check-empty@v3.0.1 # the latest version on the GitHub Actions
|
|
145
146
|
# marketplace; this step will fail and subsequent jobs will not run if any file is
|
|
146
147
|
# not empty
|
|
147
148
|
with:
|
|
@@ -7,26 +7,22 @@ Pre-commit hook, command-line tool and GitHub Action all-in-one.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
from contextlib import suppress
|
|
12
|
-
|
|
13
|
-
from . import constants, reporter
|
|
10
|
+
from check_empty import constants, reporter
|
|
14
11
|
|
|
12
|
+
__all__ = ('Handler', 'check', 'default_reporter')
|
|
13
|
+
__version__ = '3.0.1'
|
|
14
|
+
"""The version of the package."""
|
|
15
|
+
default_reporter: reporter.Reporter = reporter.Reporter()
|
|
16
|
+
"""The default reporter used by :func:`check`."""
|
|
15
17
|
TYPE_CHECKING = False
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
19
|
from collections.abc import Callable, Iterable
|
|
18
|
-
from types import
|
|
20
|
+
from types import TracebackType
|
|
19
21
|
from typing import Any, SupportsIndex
|
|
20
22
|
|
|
21
23
|
from _typeshed import FileDescriptorOrPath
|
|
22
24
|
from typing_extensions import Self
|
|
23
25
|
|
|
24
|
-
__all__ = ('Handler', 'check', 'default_reporter')
|
|
25
|
-
__version__ = '2.0.0'
|
|
26
|
-
"""The version of the package."""
|
|
27
|
-
default_reporter: reporter.Reporter = reporter.Reporter()
|
|
28
|
-
"""The default reporter used by :func:`check`."""
|
|
29
|
-
|
|
30
26
|
|
|
31
27
|
class Handler:
|
|
32
28
|
"""Used to handle errors when opening a file."""
|
|
@@ -36,22 +32,18 @@ class Handler:
|
|
|
36
32
|
"""The file descriptor or string path to the file being handled."""
|
|
37
33
|
c: bool
|
|
38
34
|
"""Whether an I/O error was handled."""
|
|
39
|
-
f: Callable[[
|
|
40
|
-
"""The function to call
|
|
41
|
-
|
|
42
|
-
Should take a string representing the error message as the only argument and
|
|
43
|
-
preferably return ``None``.
|
|
44
|
-
"""
|
|
45
|
-
j: Callable[[str], None]
|
|
35
|
+
f: Callable[[OSError], object]
|
|
36
|
+
"""The function to call on the I/O error being handled."""
|
|
37
|
+
j: Callable[[str], object]
|
|
46
38
|
"""The function to call when handling the case where the file is missing.
|
|
47
39
|
|
|
48
40
|
Should take the path to the file or a string encapsulating the validity of the file
|
|
49
|
-
descriptor as the only argument
|
|
41
|
+
descriptor as the only argument.
|
|
50
42
|
"""
|
|
51
43
|
v: str | None
|
|
52
44
|
"""The cached value of :attr:`e`."""
|
|
53
45
|
|
|
54
|
-
def __init__(self, *a: Any)
|
|
46
|
+
def __init__(self, *a: Any): # ruff: ignore[any-type]
|
|
55
47
|
"""Initialize the handler."""
|
|
56
48
|
self.j, self.f, self.a = a
|
|
57
49
|
self.v = None
|
|
@@ -71,7 +63,7 @@ class Handler:
|
|
|
71
63
|
...
|
|
72
64
|
|
|
73
65
|
@property
|
|
74
|
-
def e(self, s: str = 'invalid fd (negative): %d', t: str = 'fd: %d') -> str:
|
|
66
|
+
def e(self, s: str = 'invalid fd (negative): %d', t: str = 'fd: %d') -> str:
|
|
75
67
|
"""The file path itself, or the file descriptor coerced to a string."""
|
|
76
68
|
r = self.v
|
|
77
69
|
if r is None:
|
|
@@ -92,7 +84,7 @@ class Handler:
|
|
|
92
84
|
"""
|
|
93
85
|
if t is None or not isinstance(v, OSError):
|
|
94
86
|
return False
|
|
95
|
-
self.j(self.e) if v.errno == constants.ENOENT else self.f(
|
|
87
|
+
self.j(self.e) if v.errno == constants.ENOENT else self.f(v)
|
|
96
88
|
self.c = True
|
|
97
89
|
return True
|
|
98
90
|
|
|
@@ -104,7 +96,7 @@ def check(
|
|
|
104
96
|
may_not_exist: bool = False,
|
|
105
97
|
verbosity: SupportsIndex = 2,
|
|
106
98
|
recurse_into: SupportsIndex = constants.RecurseInto.NONE,
|
|
107
|
-
reporter: reporter.
|
|
99
|
+
reporter: reporter.abcdef.ReporterABC = default_reporter,
|
|
108
100
|
) -> int:
|
|
109
101
|
# ruff: ignore[docstring-missing-exception]
|
|
110
102
|
r"""Check the emptiness of files, recursing into directories if passed.
|
|
@@ -130,13 +122,15 @@ def check(
|
|
|
130
122
|
The integer exit code calculated by the reporter.
|
|
131
123
|
|
|
132
124
|
"""
|
|
125
|
+
from contextlib import suppress
|
|
126
|
+
from os import fsdecode, scandir, stat
|
|
127
|
+
|
|
133
128
|
files = list(files)
|
|
134
129
|
if not files:
|
|
135
130
|
return 0
|
|
136
131
|
k, j, t, n, o = [0] * 5, [], 0, len(files), files.pop
|
|
137
132
|
u, e, _ = j.extend, j.pop, lambda v: lambda _, k=k: k.__setitem__(v, k[v] + 1)
|
|
138
133
|
verbosity, recurse_into = (type(x).__index__(x) for x in (verbosity, recurse_into))
|
|
139
|
-
# ruff: disable[magic-value-comparison]
|
|
140
134
|
if verbosity > 4:
|
|
141
135
|
b = ['']
|
|
142
136
|
f = b.append
|
|
@@ -149,7 +143,7 @@ def check(
|
|
|
149
143
|
v = ra = None
|
|
150
144
|
x = aa = lambda _: None
|
|
151
145
|
if verbosity > 2:
|
|
152
|
-
z, w = [''], [
|
|
146
|
+
z, w = [''], []
|
|
153
147
|
i = z.append, w.append
|
|
154
148
|
else:
|
|
155
149
|
z = w = None
|
|
@@ -159,11 +153,10 @@ def check(
|
|
|
159
153
|
g = r.append
|
|
160
154
|
else:
|
|
161
155
|
r, g = None, _(3)
|
|
162
|
-
# ruff: enable[magic-value-comparison]
|
|
163
156
|
zp, tr, rr, z7, ar, lh, ac = map(recurse_into.__and__, constants.ARCHIVE_FORMATS)
|
|
164
157
|
an = bool(recurse_into)
|
|
165
158
|
if an:
|
|
166
|
-
from
|
|
159
|
+
from check_empty import archives as av
|
|
167
160
|
md = 'a' if clear else 'r'
|
|
168
161
|
if TYPE_CHECKING:
|
|
169
162
|
import tarfile as tr
|
|
@@ -208,20 +201,19 @@ def check(
|
|
|
208
201
|
else:
|
|
209
202
|
f(a)
|
|
210
203
|
|
|
211
|
-
def v(
|
|
204
|
+
def v(f: Callable[[str], Any], e: type[BaseException]) -> bool:
|
|
212
205
|
nonlocal d
|
|
213
|
-
with suppress(
|
|
214
|
-
d =
|
|
206
|
+
with suppress(e):
|
|
207
|
+
d = f(a)
|
|
215
208
|
return True
|
|
216
209
|
return False
|
|
217
210
|
|
|
218
|
-
if tr and v(tr,
|
|
211
|
+
if tr and v(tr.open, tr.TarError):
|
|
219
212
|
aa(a)
|
|
220
213
|
for r in d.getmembers():
|
|
221
214
|
a, s = q = r.name, r.size
|
|
222
215
|
e()
|
|
223
|
-
if clear
|
|
224
|
-
av.purge_tar(d)
|
|
216
|
+
av.purge_tar(d) if clear else d.close()
|
|
225
217
|
elif zp and zp.is_zipfile(a):
|
|
226
218
|
with zp.ZipFile(a, md) as d:
|
|
227
219
|
aa(a)
|
|
@@ -248,14 +240,14 @@ def check(
|
|
|
248
240
|
for r in d.infolist():
|
|
249
241
|
a, s = q = r.filename, r.file_size
|
|
250
242
|
w()
|
|
251
|
-
elif ar and v(ar,
|
|
243
|
+
elif ar and v(ar.Archive, ar.ArchiveFormatError):
|
|
252
244
|
aa(a)
|
|
253
245
|
for r in d.infolist():
|
|
254
246
|
a, s = q = r.name, r.size
|
|
255
247
|
e()
|
|
256
248
|
if clear:
|
|
257
249
|
av.purge_ar(d, ar.HEADER_GNU)
|
|
258
|
-
elif ac and v(ac,
|
|
250
|
+
elif ac and v(ac.open, ac.AceError):
|
|
259
251
|
aa(a)
|
|
260
252
|
for r in filter(ac.AceMember.is_reg, d):
|
|
261
253
|
a, s = q = r.filename, r.size
|
|
@@ -278,24 +270,25 @@ def check(
|
|
|
278
270
|
try:
|
|
279
271
|
return an and ha(a)
|
|
280
272
|
except Exception as e: # ruff: ignore[blind-except]
|
|
281
|
-
i[1](
|
|
273
|
+
i[1](e)
|
|
282
274
|
return True
|
|
283
275
|
|
|
284
276
|
while files:
|
|
285
277
|
a, q = o(), None
|
|
286
278
|
m = isinstance(a, int)
|
|
287
279
|
if not m:
|
|
288
|
-
a =
|
|
280
|
+
a = fsdecode(a)
|
|
289
281
|
with Handler(*i, a) as h:
|
|
290
|
-
q =
|
|
282
|
+
q = stat(a) # ruff: ignore[os-stat]
|
|
291
283
|
if q is None:
|
|
292
284
|
continue
|
|
293
285
|
c = h.e
|
|
294
286
|
if q.st_mode & constants.TYPE_MASK == constants.S_IFDIR:
|
|
295
287
|
if m:
|
|
296
288
|
raise constants.DIRFD_UNSUPPORTED
|
|
297
|
-
|
|
298
|
-
|
|
289
|
+
with h:
|
|
290
|
+
u(scandir(c))
|
|
291
|
+
x(c)
|
|
299
292
|
continue
|
|
300
293
|
if isinstance(a, str) and hb(a):
|
|
301
294
|
continue
|
|
@@ -312,8 +305,9 @@ def check(
|
|
|
312
305
|
m = e()
|
|
313
306
|
a = m.path
|
|
314
307
|
if m.is_dir():
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
with Handler(*i, a):
|
|
309
|
+
u(scandir(a))
|
|
310
|
+
x(a)
|
|
317
311
|
continue
|
|
318
312
|
if hb(a):
|
|
319
313
|
continue
|
|
@@ -326,8 +320,7 @@ def check(
|
|
|
326
320
|
t += s
|
|
327
321
|
if clear:
|
|
328
322
|
Handler(*i, a).o()
|
|
329
|
-
|
|
330
|
-
x, d = k[1] if z is None else len(z) - 1, k[2] if w is None else len(w) - 1
|
|
323
|
+
x, d = k[1] if z is None else len(z) - 1, k[2] if w is None else len(w)
|
|
331
324
|
y, p = k[3] if r is None else len(r), k[0] if b is None else len(b) - 1
|
|
332
325
|
reporter.report(z, w, v, b, r, x, y, d, p, n, t, verbosity, clear)
|
|
333
326
|
reporter.reset_out()
|
|
@@ -4,9 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import check_empty as c
|
|
6
6
|
|
|
7
|
-
TYPE_CHECKING = False
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from collections.abc import Iterable
|
|
10
7
|
__all__ = ('main',)
|
|
11
8
|
_p = __import__('argparse').ArgumentParser(
|
|
12
9
|
'check-empty',
|
|
@@ -43,6 +40,9 @@ f('-a', '--ar', action='store_true', help='recurse into .a, .ar and .lib archive
|
|
|
43
40
|
f('-7', '--7z', action='store_true', dest='z7', help='recurse into .7z archives')
|
|
44
41
|
f('-l', '--lzh', action='store_true', help='recurse into .lzh and .lha archives')
|
|
45
42
|
f('-A', '--ace', action='store_true', help='recurse into .ace archives')
|
|
43
|
+
TYPE_CHECKING = False
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from collections.abc import Iterable
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _r():
|
|
@@ -68,15 +68,16 @@ def main(argv: Iterable[str] | None = None) -> int:
|
|
|
68
68
|
# ruff: ignore[open-file-with-context-handler]
|
|
69
69
|
c.default_reporter.to(open(o, 'w', encoding='utf-8'))
|
|
70
70
|
m = c.constants
|
|
71
|
-
r = m.RecurseInto
|
|
72
|
-
for a in m.ARCHIVE_FORMATS:
|
|
73
|
-
if getattr(n, a._name_.lower()):
|
|
74
|
-
r |= a
|
|
71
|
+
r = m.RecurseInto
|
|
75
72
|
return c.check(
|
|
76
73
|
n.filenames,
|
|
77
74
|
clear=n.clear,
|
|
78
75
|
may_not_exist=n.may_not_exist,
|
|
79
|
-
recurse_into=
|
|
76
|
+
recurse_into=__import__('functools').reduce(
|
|
77
|
+
r.__or__,
|
|
78
|
+
filter(lambda a: getattr(n, a.name.lower()), m.ARCHIVE_FORMATS),
|
|
79
|
+
r.NONE,
|
|
80
|
+
),
|
|
80
81
|
verbosity=2 + n.verbose - n.quiet,
|
|
81
82
|
)
|
|
82
83
|
|
|
@@ -3,23 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
from itertools import filterfalse
|
|
7
|
-
from operator import attrgetter
|
|
8
6
|
from tempfile import NamedTemporaryFile
|
|
9
7
|
|
|
10
|
-
TYPE_CHECKING = False
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
import tarfile
|
|
13
|
-
import zipfile
|
|
14
|
-
from collections.abc import Callable, Iterable
|
|
15
|
-
from typing import Final, Literal
|
|
16
|
-
|
|
17
|
-
import arpy
|
|
18
|
-
import py7zr
|
|
19
8
|
__all__ = (
|
|
20
9
|
'AR_FS',
|
|
21
10
|
'AR_MAG',
|
|
22
11
|
'AR_NAME_SIZE',
|
|
12
|
+
'AR_SKIP',
|
|
23
13
|
'clear_file_in_zip',
|
|
24
14
|
'purge_7z',
|
|
25
15
|
'purge_ar',
|
|
@@ -27,13 +17,26 @@ __all__ = (
|
|
|
27
17
|
)
|
|
28
18
|
_ = __import__('io').BytesIO()
|
|
29
19
|
AR_FS: Final = b'%-16b0 0 0 100644 %-10d`\n%b'
|
|
20
|
+
"""A C-style format string used to create file headers and bodies in ar archives."""
|
|
30
21
|
AR_MAG: Final = b'!<arch>\n'
|
|
22
|
+
"""The magic number for ar archives."""
|
|
31
23
|
AR_NAME_SIZE: Final = 16
|
|
24
|
+
"""The maximum length of a file name in an ar archive before special handling."""
|
|
32
25
|
AR_SKIP: Final[
|
|
33
26
|
frozenset[
|
|
34
27
|
Literal[b'/', b'//', b'/SYM64/', b'__.SYMDEF', b'__.SYMDEF SORTED'] | None
|
|
35
28
|
]
|
|
36
29
|
] = frozenset((b'/', b'//', b'/SYM64/', b'__.SYMDEF', b'__.SYMDEF SORTED', None))
|
|
30
|
+
"""The file names to skip when clearing files in an ar archive."""
|
|
31
|
+
TYPE_CHECKING = False
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
import tarfile
|
|
34
|
+
import zipfile
|
|
35
|
+
from collections.abc import Callable, Iterable
|
|
36
|
+
from typing import Final, Literal
|
|
37
|
+
|
|
38
|
+
import py7zr
|
|
39
|
+
from arpy import Archive
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
def clear_file_in_zip(z: zipfile.ZipFile, i: zipfile.ZipInfo) -> None:
|
|
@@ -71,7 +74,7 @@ def purge_tar(t: tarfile.TarFile) -> None:
|
|
|
71
74
|
os.replace(p.name, x)
|
|
72
75
|
|
|
73
76
|
|
|
74
|
-
def purge_ar(a:
|
|
77
|
+
def purge_ar(a: Archive, g: int) -> None:
|
|
75
78
|
"""Clear the contents of each file in a .a, .ar or .lib archive."""
|
|
76
79
|
a.read_all_headers()
|
|
77
80
|
i, n = a.headers, a.file.name
|
|
@@ -83,7 +86,9 @@ def purge_ar(a: arpy.Archive, g: int) -> None:
|
|
|
83
86
|
with NamedTemporaryFile(suffix='.a', delete=False) as f:
|
|
84
87
|
w = f.write
|
|
85
88
|
w(AR_MAG)
|
|
86
|
-
q =
|
|
89
|
+
q = __import__('itertools').filterfalse(
|
|
90
|
+
AR_SKIP.__contains__, map(__import__('operator').attrgetter('name'), i)
|
|
91
|
+
)
|
|
87
92
|
_purge_ar_gnu(tuple(q), w) if i[0].type == g else _purge_ar_bsd(q, w)
|
|
88
93
|
os.replace(f.name, n)
|
|
89
94
|
|
|
@@ -4,9 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
|
|
7
|
-
TYPE_CHECKING = False
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from typing import Final
|
|
10
7
|
__all__ = (
|
|
11
8
|
'DIRFD_UNSUPPORTED',
|
|
12
9
|
'ENOENT',
|
|
@@ -15,8 +12,11 @@ __all__ = (
|
|
|
15
12
|
'TYPE_MASK',
|
|
16
13
|
'RecurseInto',
|
|
17
14
|
)
|
|
15
|
+
TYPE_CHECKING = False
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from typing import Final
|
|
18
18
|
TYPE_MASK: Final = 0xF000
|
|
19
|
-
"""
|
|
19
|
+
"""The mask that extracts the type of a file from its mode bits from :func:`os.stat`."""
|
|
20
20
|
S_IFDIR: Final = 0x4000
|
|
21
21
|
"""Equal to :data:`stat.S_IFDIR`."""
|
|
22
22
|
ENOENT: Final = 2
|
|
@@ -58,7 +58,7 @@ class RecurseInto(__import__('enum').IntFlag):
|
|
|
58
58
|
"""Recurse into all archive types."""
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
ARCHIVE_FORMATS: Final = tuple(
|
|
61
|
+
ARCHIVE_FORMATS: Final[tuple[RecurseInto, ...]] = tuple(
|
|
62
62
|
filter(lambda x: x > 0 == x & (x - 1), RecurseInto)
|
|
63
63
|
if __import__('sys').version_info < (3, 11)
|
|
64
64
|
else RecurseInto
|
|
@@ -5,19 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
import itertools as i
|
|
6
6
|
import sys as s
|
|
7
7
|
|
|
8
|
-
from . import
|
|
9
|
-
|
|
10
|
-
TYPE_CHECKING = False
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from typing import IO
|
|
8
|
+
from check_empty.reporter import abcdef, dct as dct
|
|
13
9
|
|
|
14
10
|
__all__ = ('DelayedReporter', 'Reporter')
|
|
11
|
+
TYPE_CHECKING = False
|
|
15
12
|
|
|
16
13
|
|
|
17
|
-
class Reporter(
|
|
14
|
+
class Reporter(abcdef.ReporterABC):
|
|
18
15
|
"""Concrete and default reporter implementation that prints output to a stream."""
|
|
19
16
|
|
|
20
17
|
if TYPE_CHECKING:
|
|
18
|
+
from typing import IO
|
|
19
|
+
|
|
21
20
|
o: IO[str] | None
|
|
22
21
|
|
|
23
22
|
def to(self, out: IO[str] | None = None) -> IO[str] | None:
|
|
@@ -35,7 +34,7 @@ class Reporter(abc.ReporterABC):
|
|
|
35
34
|
def report( # ruff: ignore[too-many-arguments, too-many-positional-arguments]
|
|
36
35
|
self,
|
|
37
36
|
z: list[str] | None,
|
|
38
|
-
w: list[
|
|
37
|
+
w: list[OSError] | None,
|
|
39
38
|
a: list[str] | None,
|
|
40
39
|
b: list[str] | None,
|
|
41
40
|
r: list[tuple[str, int]] | None,
|
|
@@ -68,7 +67,7 @@ class Reporter(abc.ReporterABC):
|
|
|
68
67
|
else '\nAll files were found'
|
|
69
68
|
)
|
|
70
69
|
if w is not None:
|
|
71
|
-
q('\nError: '.join(w))
|
|
70
|
+
q('\nError: '.join(i.chain(('',), map(str, w))))
|
|
72
71
|
if d:
|
|
73
72
|
q(f'\n{d} I/O error{"s" if d > 1 else ""} encountered')
|
|
74
73
|
if b is not None:
|
|
@@ -93,5 +92,8 @@ class Reporter(abc.ReporterABC):
|
|
|
93
92
|
q.close()
|
|
94
93
|
|
|
95
94
|
|
|
96
|
-
class DelayedReporter(
|
|
95
|
+
class DelayedReporter(abcdef.DelayedReporterMixin, Reporter):
|
|
97
96
|
"""The delayed version of the default reporter."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
del TYPE_CHECKING
|
check_empty-2.0.0/check_empty/reporter/abc.py → check_empty-3.0.1/check_empty/reporter/abcdef.py
RENAMED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
"""Abstract base classes for reporters."""
|
|
1
|
+
"""Abstract base classes definitions ("abcdef") for reporters."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import abc as a
|
|
6
6
|
|
|
7
|
-
from .exceptions import NoReport
|
|
7
|
+
from check_empty.reporter.exceptions import NoReport
|
|
8
8
|
|
|
9
|
+
__all__ = ('DelayedReporterMixin', 'ReporterABC')
|
|
9
10
|
TYPE_CHECKING = False
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
13
|
-
__all__ = ('DelayedReporterMixin', 'ReporterABC')
|
|
14
|
-
|
|
15
14
|
|
|
16
15
|
class ReporterABC(metaclass=a.ABCMeta):
|
|
17
16
|
"""Abstract base class for all reporters accepted by :func:`~check_empty.check`."""
|
|
@@ -23,7 +22,7 @@ class ReporterABC(metaclass=a.ABCMeta):
|
|
|
23
22
|
def report( # ruff: ignore[too-many-arguments, too-many-positional-arguments]
|
|
24
23
|
self,
|
|
25
24
|
z: list[str] | None,
|
|
26
|
-
w: list[
|
|
25
|
+
w: list[OSError] | None,
|
|
27
26
|
a: list[str] | None,
|
|
28
27
|
b: list[str] | None,
|
|
29
28
|
r: list[tuple[str, int]] | None,
|
|
@@ -41,7 +40,7 @@ class ReporterABC(metaclass=a.ABCMeta):
|
|
|
41
40
|
|
|
42
41
|
Args:
|
|
43
42
|
z: list of paths to files that were not found.
|
|
44
|
-
w: list of
|
|
43
|
+
w: list of I/O errors when processing non-missing files.
|
|
45
44
|
a: list of directories that were recursed into.
|
|
46
45
|
b: list of empty file paths.
|
|
47
46
|
r: list of 2-tuples representing non-empty files and their sizes.
|
|
@@ -127,3 +126,6 @@ class DelayedReporterMixin(ReporterABC):
|
|
|
127
126
|
if a is None:
|
|
128
127
|
raise NoReport(self)
|
|
129
128
|
super().report(*a)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
del TYPE_CHECKING
|
check_empty-2.0.0/check_empty/reporter/dict.py → check_empty-3.0.1/check_empty/reporter/dct.py
RENAMED
|
@@ -4,16 +4,15 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import itertools as i
|
|
6
6
|
|
|
7
|
-
from . import exceptions as _
|
|
8
|
-
from .
|
|
7
|
+
from check_empty.reporter import exceptions as _
|
|
8
|
+
from check_empty.reporter.abcdef import ReporterABC
|
|
9
9
|
|
|
10
|
+
__all__ = ('DictReporter', 'SelfReporter')
|
|
10
11
|
TYPE_CHECKING = False
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from collections.abc import Iterable, MutableMapping
|
|
13
14
|
from typing import Any, Final
|
|
14
15
|
|
|
15
|
-
__all__ = ('DictReporter', 'SelfReporter')
|
|
16
|
-
|
|
17
16
|
|
|
18
17
|
class DictReporter(ReporterABC):
|
|
19
18
|
"""Dump the metrics into a dictionary-like object."""
|
|
@@ -63,7 +62,7 @@ class DictReporter(ReporterABC):
|
|
|
63
62
|
The previous output mapping, or ``None`` if no redirection is in effect.
|
|
64
63
|
"""
|
|
65
64
|
|
|
66
|
-
def __init__(self, keys: Iterable[str | None] | None = None)
|
|
65
|
+
def __init__(self, keys: Iterable[str | None] | None = None):
|
|
67
66
|
"""Initialize the reporter.
|
|
68
67
|
|
|
69
68
|
Args:
|
|
@@ -139,3 +138,6 @@ class SelfReporter(DictReporter):
|
|
|
139
138
|
This is only implemented at type-check time because the exact attributes
|
|
140
139
|
and their types are not statically known.
|
|
141
140
|
"""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
del TYPE_CHECKING
|
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
TYPE_CHECKING = False
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from typing import ClassVar
|
|
8
|
-
|
|
9
|
-
from .abc import DelayedReporterMixin, ReporterABC
|
|
10
|
-
from .dict import DictReporter
|
|
11
|
-
|
|
12
5
|
__all__ = (
|
|
13
6
|
'IncorrectKeynames',
|
|
14
7
|
'IncorrectKeynamesLength',
|
|
@@ -17,6 +10,12 @@ __all__ = (
|
|
|
17
10
|
'NoReport',
|
|
18
11
|
'ReporterError',
|
|
19
12
|
)
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import ClassVar
|
|
16
|
+
|
|
17
|
+
from check_empty.reporter.abcdef import DelayedReporterMixin, ReporterABC
|
|
18
|
+
from check_empty.reporter.dct import DictReporter
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class ReporterError(Exception):
|
|
@@ -27,7 +26,7 @@ class ReporterError(Exception):
|
|
|
27
26
|
message: ClassVar[str]
|
|
28
27
|
"""The message of the exception."""
|
|
29
28
|
|
|
30
|
-
def __init__(self, reporter: ReporterABC)
|
|
29
|
+
def __init__(self, reporter: ReporterABC):
|
|
31
30
|
"""Initialize the exception.
|
|
32
31
|
|
|
33
32
|
Args:
|
|
@@ -46,7 +45,7 @@ class NoReport(ReporterError):
|
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
class IncorrectKeynames(ReporterError):
|
|
49
|
-
"""A :class:`check_empty.reporter.
|
|
48
|
+
"""A :class:`check_empty.reporter.dct.DictReporter` got invalid ``keys``."""
|
|
50
49
|
|
|
51
50
|
reporter: DictReporter
|
|
52
51
|
"""The reporter concerned."""
|
|
@@ -65,6 +64,11 @@ class IncorrectKeynamesType(IncorrectKeynames):
|
|
|
65
64
|
|
|
66
65
|
|
|
67
66
|
class NoOutputMapping(ReporterError):
|
|
68
|
-
"""A :class:`check_empty.reporter.
|
|
67
|
+
"""A :class:`check_empty.reporter.dct.DictReporter` had no output mapping."""
|
|
69
68
|
|
|
69
|
+
reporter: DictReporter
|
|
70
|
+
"""The reporter concerned."""
|
|
70
71
|
message = 'no mapping to output to'
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
del TYPE_CHECKING
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["uv_build>=0.12.
|
|
2
|
+
requires = ["uv_build>=0.12.19"]
|
|
3
3
|
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[dependency-groups]
|
|
@@ -55,7 +55,7 @@ license-files = [
|
|
|
55
55
|
name = "check-empty"
|
|
56
56
|
readme = "README.md"
|
|
57
57
|
requires-python = ">=3.10"
|
|
58
|
-
version = "
|
|
58
|
+
version = "3.0.1"
|
|
59
59
|
|
|
60
60
|
[[project.authors]]
|
|
61
61
|
name = "Jonathan Dung"
|
|
@@ -68,7 +68,7 @@ email = "jonathandung@yahoo.com"
|
|
|
68
68
|
[project.optional-dependencies]
|
|
69
69
|
7z = ["py7zr==1.1.3"]
|
|
70
70
|
ace = ["acefile==0.6.14"]
|
|
71
|
-
all = ["check-empty[7z,ace,ar,lzh,rar]==
|
|
71
|
+
all = ["check-empty[7z,ace,ar,lzh,rar]==3.0.1"]
|
|
72
72
|
ar = ["arpy==2.4.0"]
|
|
73
73
|
lzh = ["lhafile==0.3.1"]
|
|
74
74
|
rar = ["rarfile==4.5"]
|
|
@@ -77,7 +77,7 @@ rar = ["rarfile==4.5"]
|
|
|
77
77
|
check-empty = "check_empty.__main__:main"
|
|
78
78
|
|
|
79
79
|
[tool.ruff]
|
|
80
|
-
required-version = ">=0.16.
|
|
80
|
+
required-version = ">=0.16.9"
|
|
81
81
|
target-version = "py310"
|
|
82
82
|
|
|
83
83
|
[tool.ruff.analyze]
|
|
@@ -95,17 +95,12 @@ ignore = [
|
|
|
95
95
|
"builtin-open",
|
|
96
96
|
"complex-structure",
|
|
97
97
|
"error-suffix-on-exception-name",
|
|
98
|
-
"implicit-namespace-package",
|
|
99
98
|
"import-outside-top-level",
|
|
100
99
|
"incorrect-blank-line-before-class",
|
|
101
100
|
"meta-class-abc-meta",
|
|
102
101
|
"multi-line-summary-second-line",
|
|
103
102
|
"non-empty-init-module",
|
|
104
103
|
"os-replace",
|
|
105
|
-
"too-many-branches",
|
|
106
|
-
"too-many-locals",
|
|
107
|
-
"too-many-statements",
|
|
108
|
-
"unnecessary-dunder-call",
|
|
109
104
|
]
|
|
110
105
|
preview = true
|
|
111
106
|
select = ["ALL"]
|
|
@@ -115,6 +110,9 @@ docstring-quotes = "double"
|
|
|
115
110
|
inline-quotes = "single"
|
|
116
111
|
multiline-quotes = "double"
|
|
117
112
|
|
|
113
|
+
[tool.ruff.lint.flake8-annotations]
|
|
114
|
+
mypy-init-return = true
|
|
115
|
+
|
|
118
116
|
[tool.ruff.lint.isort]
|
|
119
117
|
combine-as-imports = true
|
|
120
118
|
|
|
@@ -123,20 +121,18 @@ combine-as-imports = true
|
|
|
123
121
|
"D",
|
|
124
122
|
"missing-type-args",
|
|
125
123
|
]
|
|
124
|
+
"!check_empty/__init__.py" = ["missing-copyright-notice"]
|
|
125
|
+
"check_empty/__init__.py" = ["PLR"]
|
|
126
|
+
"check_empty/__main__.py" = ["missing-return-type-private-function"]
|
|
127
|
+
"docs/source/conf.py" = ["implicit-namespace-package"]
|
|
126
128
|
"test_check_empty.py" = [
|
|
127
129
|
"ANN",
|
|
128
130
|
"D",
|
|
129
131
|
]
|
|
130
|
-
"!check_empty/__init__.py" = ["missing-copyright-notice"]
|
|
131
|
-
"check_empty/__main__.py" = ["missing-return-type-private-function"]
|
|
132
132
|
|
|
133
133
|
[tool.ruff.lint.pydocstyle]
|
|
134
134
|
convention = "google"
|
|
135
135
|
|
|
136
|
-
[tool.ruff.lint.pylint]
|
|
137
|
-
max-args = 6
|
|
138
|
-
max-nested-blocks = 6
|
|
139
|
-
|
|
140
136
|
[tool.ty.analysis]
|
|
141
137
|
respect-type-ignore-comments = false
|
|
142
138
|
|
|
@@ -157,7 +153,7 @@ redundant-condition = "ignore"
|
|
|
157
153
|
unsound-assignment = "ignore"
|
|
158
154
|
|
|
159
155
|
[tool.uv]
|
|
160
|
-
required-version = ">=0.12.
|
|
156
|
+
required-version = ">=0.12.19"
|
|
161
157
|
|
|
162
158
|
[tool.uv.build-backend]
|
|
163
159
|
module-name = "check_empty"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["uv_build>=0.12.
|
|
2
|
+
requires = ["uv_build>=0.12.19"]
|
|
3
3
|
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[dependency-groups]
|
|
@@ -16,12 +16,12 @@ maintainers = [{name = "Jonathan Dung", email = "jonathandung@yahoo.com"}]
|
|
|
16
16
|
name = "check-empty"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
requires-python = ">=3.10"
|
|
19
|
-
version = "
|
|
19
|
+
version = "3.0.1"
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
"7z" = ["py7zr==1.1.3"]
|
|
23
23
|
ace = ["acefile==0.6.14"]
|
|
24
|
-
all = ["check-empty[7z,ace,ar,lzh,rar]==
|
|
24
|
+
all = ["check-empty[7z,ace,ar,lzh,rar]==3.0.1"]
|
|
25
25
|
ar = ["arpy==2.4.0"]
|
|
26
26
|
lzh = ["lhafile==0.3.1"]
|
|
27
27
|
rar = ["rarfile==4.5"]
|
|
@@ -30,7 +30,7 @@ rar = ["rarfile==4.5"]
|
|
|
30
30
|
check-empty = "check_empty.__main__:main"
|
|
31
31
|
|
|
32
32
|
[tool.ruff]
|
|
33
|
-
required-version = ">=0.16.
|
|
33
|
+
required-version = ">=0.16.9"
|
|
34
34
|
target-version = "py310"
|
|
35
35
|
|
|
36
36
|
[tool.ruff.analyze]
|
|
@@ -40,7 +40,7 @@ type-checking-imports = false
|
|
|
40
40
|
quote-style = "single"
|
|
41
41
|
|
|
42
42
|
[tool.ruff.lint]
|
|
43
|
-
ignore = ["COM", "FBT", "PT", "bad-quotes-inline-string", "builtin-open", "complex-structure", "error-suffix-on-exception-name", "
|
|
43
|
+
ignore = ["COM", "FBT", "PT", "bad-quotes-inline-string", "builtin-open", "complex-structure", "error-suffix-on-exception-name", "import-outside-top-level", "incorrect-blank-line-before-class", "meta-class-abc-meta", "multi-line-summary-second-line", "non-empty-init-module", "os-replace"]
|
|
44
44
|
preview = true
|
|
45
45
|
select = ["ALL"]
|
|
46
46
|
|
|
@@ -49,22 +49,23 @@ docstring-quotes = "double"
|
|
|
49
49
|
inline-quotes = "single"
|
|
50
50
|
multiline-quotes = "double"
|
|
51
51
|
|
|
52
|
+
[tool.ruff.lint.flake8-annotations]
|
|
53
|
+
mypy-init-return = true
|
|
54
|
+
|
|
52
55
|
[tool.ruff.lint.isort]
|
|
53
56
|
combine-as-imports = true
|
|
54
57
|
|
|
55
58
|
[tool.ruff.lint.per-file-ignores]
|
|
56
59
|
"action.py" = ["D", "missing-type-args"]
|
|
57
|
-
"test_check_empty.py" = ["ANN", "D"]
|
|
58
60
|
"!check_empty/__init__.py" = ["missing-copyright-notice"]
|
|
61
|
+
"check_empty/__init__.py" = ["PLR"]
|
|
59
62
|
"check_empty/__main__.py" = ["missing-return-type-private-function"]
|
|
63
|
+
"docs/source/conf.py" = ["implicit-namespace-package"]
|
|
64
|
+
"test_check_empty.py" = ["ANN", "D"]
|
|
60
65
|
|
|
61
66
|
[tool.ruff.lint.pydocstyle]
|
|
62
67
|
convention = "google"
|
|
63
68
|
|
|
64
|
-
[tool.ruff.lint.pylint]
|
|
65
|
-
max-args = 6
|
|
66
|
-
max-nested-blocks = 6
|
|
67
|
-
|
|
68
69
|
[tool.ty.analysis]
|
|
69
70
|
respect-type-ignore-comments = false
|
|
70
71
|
|
|
@@ -81,7 +82,7 @@ include = ["check_empty/__init__.py"]
|
|
|
81
82
|
rules = {possibly-unresolved-reference = "ignore", redundant-condition = "ignore", unsound-assignment = "ignore"}
|
|
82
83
|
|
|
83
84
|
[tool.uv]
|
|
84
|
-
required-version = ">=0.12.
|
|
85
|
+
required-version = ">=0.12.19"
|
|
85
86
|
|
|
86
87
|
[tool.uv.build-backend]
|
|
87
88
|
module-name = "check_empty"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|