check-empty 1.2.2__tar.gz → 1.2.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: check-empty
3
- Version: 1.2.2
3
+ Version: 1.2.3
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
@@ -115,7 +115,7 @@ As a pre-commit hook:
115
115
  # .pre-commit-config.yaml
116
116
  repos:
117
117
  - repo: https://github.com/jonathandung/check-empty
118
- rev: v1.2.2 # repository version
118
+ rev: v1.2.3 # repository version
119
119
  hooks:
120
120
  - id: check-empty # the hook
121
121
  args: # example list of arguments
@@ -131,7 +131,7 @@ equivalent in `prek.toml` format:
131
131
  ```toml
132
132
  [[repos]]
133
133
  repo = "https://github.com/jonathandung/check-empty"
134
- rev = "v1.2.2"
134
+ rev = "v1.2.3"
135
135
 
136
136
  [[repos.hooks]]
137
137
  id = "check-empty"
@@ -155,7 +155,7 @@ or (TOML 1.1+):
155
155
  # using multiline inline tables
156
156
  [[repos]]
157
157
  repo = "https://github.com/jonathandung/check-empty"
158
- rev = "v1.2.2"
158
+ rev = "v1.2.3"
159
159
  hooks = [{
160
160
  id = "check-empty",
161
161
  args = ["-Q"],
@@ -175,7 +175,7 @@ As a GitHub Actions workflow step:
175
175
 
176
176
  ```yaml
177
177
  steps:
178
- - uses: jonathandung/check-empty@v1.2.2 # the latest version on the GitHub Actions
178
+ - uses: jonathandung/check-empty@v1.2.3 # the latest version on the GitHub Actions
179
179
  # marketplace; this step will fail and subsequent jobs will not run if any file is
180
180
  # not empty
181
181
  with:
@@ -80,7 +80,7 @@ As a pre-commit hook:
80
80
  # .pre-commit-config.yaml
81
81
  repos:
82
82
  - repo: https://github.com/jonathandung/check-empty
83
- rev: v1.2.2 # repository version
83
+ rev: v1.2.3 # repository version
84
84
  hooks:
85
85
  - id: check-empty # the hook
86
86
  args: # example list of arguments
@@ -96,7 +96,7 @@ equivalent in `prek.toml` format:
96
96
  ```toml
97
97
  [[repos]]
98
98
  repo = "https://github.com/jonathandung/check-empty"
99
- rev = "v1.2.2"
99
+ rev = "v1.2.3"
100
100
 
101
101
  [[repos.hooks]]
102
102
  id = "check-empty"
@@ -120,7 +120,7 @@ or (TOML 1.1+):
120
120
  # using multiline inline tables
121
121
  [[repos]]
122
122
  repo = "https://github.com/jonathandung/check-empty"
123
- rev = "v1.2.2"
123
+ rev = "v1.2.3"
124
124
  hooks = [{
125
125
  id = "check-empty",
126
126
  args = ["-Q"],
@@ -140,7 +140,7 @@ As a GitHub Actions workflow step:
140
140
 
141
141
  ```yaml
142
142
  steps:
143
- - uses: jonathandung/check-empty@v1.2.2 # the latest version on the GitHub Actions
143
+ - uses: jonathandung/check-empty@v1.2.3 # the latest version on the GitHub Actions
144
144
  # marketplace; this step will fail and subsequent jobs will not run if any file is
145
145
  # not empty
146
146
  with:
@@ -0,0 +1,276 @@
1
+ # Copyright © 2026 Jonathan Dung. All rights reserved.
2
+ # SPDX-License-Identifier: MIT
3
+ """Utility to check the emptiness of files and directories.
4
+
5
+ Pre-commit hook, command-line tool and GitHub Action all-in-one.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import sys
12
+ from itertools import chain
13
+
14
+ __all__ = ('DelayedReporter', 'Reporter', 'check', 'default_reporter')
15
+ __version__ = '1.2.3'
16
+
17
+ TYPE_CHECKING = False
18
+ if TYPE_CHECKING:
19
+ import typing
20
+ from collections.abc import Iterable
21
+
22
+ from _typeshed import FileDescriptorOrPath
23
+ _DIRECTORY_DESCRIPTOR_UNSUPPORTED: BaseException = (
24
+ SystemError('got directory descriptor on Windows')
25
+ if os.name == 'nt'
26
+ else NotImplementedError('directory descriptors are not supported')
27
+ )
28
+ _TYPE_MASK: int = 0xF000
29
+ _S_IFDIR: int = 0x4000
30
+
31
+
32
+ class _Handler:
33
+ __slots__ = 'a', 'c', 'f', 'j', 'v'
34
+
35
+ def __init__(self, *a):
36
+ self.j, self.f, self.a = a
37
+ self.v = None
38
+
39
+ def __enter__(self):
40
+ self.c = False
41
+ return self
42
+
43
+ def o(self):
44
+ with self, open(self.a, 'wb'):
45
+ ...
46
+
47
+ @property
48
+ def e(self, s='invalid fd (negative): %d', t='fd: %d'): # ruff: ignore[property-with-parameters]
49
+ r = self.v
50
+ if r is None:
51
+ a = self.a
52
+ self.v = r = (t, s)[a < 0] % a if isinstance(a, int) else os.fsdecode(a)
53
+ return r
54
+
55
+ def __exit__(self, t, v, _):
56
+ if t is None or not issubclass(t, OSError):
57
+ return False
58
+ self.j(self.e) if v.errno == 2 else self.f(str(v))
59
+ self.c = True
60
+ return True
61
+
62
+
63
+ class Reporter:
64
+ """Type of all reporters that :func:`check` accepts as the ``reporter`` argument."""
65
+ __slots__ = 'o',
66
+ o: typing.IO[str] | None
67
+
68
+ def __init__(self):
69
+ """Initialize the reporter."""
70
+ self.o = None
71
+
72
+ def to(self, out: typing.IO[str] | None = None) -> typing.IO[str] | None:
73
+ """Temporarily redirect the output of the reporter for one check.
74
+
75
+ Args:
76
+ out: The output stream to redirect to. If ``None``, the reporter will write
77
+ to :data:`sys.stdout`.
78
+
79
+ Returns:
80
+ The previous output stream.
81
+
82
+ """
83
+ self.o, o = out, self.o
84
+ return o
85
+
86
+ @staticmethod
87
+ def calculate_result(y: bool, x: bool, d: bool) -> int:
88
+ """Set the result of the check as :attr:`r`.
89
+
90
+ Returns:
91
+ The exit code of the check. For the default implementation, this is a
92
+ bitwise or of 1 (some files were not empty), 4 (some files or directories
93
+ were absent and :ref:`-m <check-empty--m>` / :ref:`--may-not-exist
94
+ <check-empty---may-not-exist>` was omitted) and 8 (caught :exc:`OSError`
95
+ while processing some files), such that 0 is correctly the only return
96
+ value that represents success. The 2 bit is skipped since 2 is the exit
97
+ code of :class:`argparse.ArgumentParser` when it encounters invalid
98
+ arguments.
99
+ """
100
+ return y | x << 2 | d << 3
101
+
102
+ def report(self, z, w, r, x, y, d, p, a, b, t, n, v, c): # ruff: ignore[too-many-arguments, too-many-positional-arguments]
103
+ """Report the results of the check.
104
+
105
+ Args:
106
+ z: List of files that were not found.
107
+ w: List of error messages for files that could not be processed.
108
+ r: List of 2-tuples representing non-empty files and their sizes.
109
+ x: Number of files that were not found.
110
+ y: Number of non-empty files.
111
+ d: Number of I/O errors encountered.
112
+ p: Number of empty files.
113
+ a: List of directories that were recursed into.
114
+ b: List of empty file paths.
115
+ t: Total size of non-empty files in bytes.
116
+ n: Total number of files checked.
117
+ v: The value of ``verbosity`` as passed to :func:`check`. For the default
118
+ implementation, verbosity > 5 is equivalent to verbosity = 5, and a
119
+ non-positive verbosity gives no output.
120
+ c: The value of ``clear`` as passed to :func:`check`.
121
+ """
122
+ q = self.o
123
+ q = (sys.stdout if q is None else q).write
124
+ if a is not None:
125
+ q('\nRecursing into directory: '.join(a))
126
+ q('\n')
127
+ if z is not None:
128
+ q('\nNot found: '.join(z))
129
+ q(
130
+ f'{x} file{"s" if x > 1 else ""} not found\n'
131
+ if x
132
+ else 'All files were found\n'
133
+ )
134
+ if w is not None:
135
+ q('\nError: '.join(w))
136
+ if d:
137
+ q(f'{d} I/O error{"s" if d > 1 else ""} encountered\n')
138
+ if b is not None:
139
+ q('\nEmpty: '.join(b))
140
+ if v > 2:
141
+ q(f'\n{p or "No"} empty file{"" if p == 1 else "s"}\n')
142
+ if y:
143
+ if r is not None:
144
+ q(
145
+ ('\nCleared: ' if c else '\nNot empty: ').join(
146
+ chain(('',), map('%s (%d bytes)'.__mod__, r))
147
+ )
148
+ )
149
+ q('\n')
150
+ q(f'{y} offending file{"s" if y > 1 else ""}\nTotal size: {t} bytes\n')
151
+ elif n > x:
152
+ q('All found files were empty\n')
153
+
154
+ def reset_stream(self) -> None:
155
+ """Reset the reporter, closing the output stream if appropriate."""
156
+ q, self.o = self.o, None
157
+ if q not in {None, sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__}:
158
+ q.close()
159
+
160
+
161
+ class DelayedReporter(Reporter):
162
+ """Only report the results only when :meth:`do_report` is called."""
163
+ __slots__ = ('a',)
164
+
165
+ def report(self, *a): # ty: ignore[invalid-method-override]
166
+ """Store the report arguments for later use."""
167
+ self.a = a
168
+
169
+ def do_report(self) -> None:
170
+ """Conduct the report for the most recent check using this reporter."""
171
+ super().report(*self.a)
172
+
173
+
174
+ default_reporter: Reporter = Reporter()
175
+ """The default reporter used by :func:`check`."""
176
+
177
+
178
+ def check(
179
+ files: Iterable[FileDescriptorOrPath],
180
+ *,
181
+ clear: bool = False,
182
+ may_not_exist: bool = False,
183
+ verbosity: typing.SupportsIndex = 2,
184
+ reporter: Reporter = default_reporter,
185
+ ) -> int:
186
+ """Check the emptiness of files, recursing into directories if passed.
187
+
188
+ Args:
189
+ files: an iterable of file descriptors or paths representing the files and
190
+ directories to check; directory descriptors (*nix) are not supported.
191
+ clear: if ``True``, clear the contents of non-empty files; directories, notably,
192
+ are not purged, but all files within should become empty.
193
+ may_not_exist: if ``True``, do not treat absent files or directories as errors.
194
+ verbosity: how much detail the program should print to stdout.
195
+ reporter: The reporter to use for reporting the results. All output of this
196
+ function is produced by the reporter.
197
+
198
+ Returns:
199
+ The integer exit code calculated by the reporter.
200
+ """
201
+ files = list(files)
202
+ if not files:
203
+ return 0
204
+ k, j, t, n, o = [0] * 4, [], 0, len(files), files.pop
205
+ u, e, _ = j.extend, j.pop, lambda v: lambda _, k=k: k.__setitem__(v, k[v] + 1)
206
+ verbosity = max(0, type(verbosity).__index__(verbosity))
207
+ if verbosity > 4:
208
+ b = ['']
209
+ f = b.append
210
+ else:
211
+ b, f = None, _(0)
212
+ if verbosity > 3:
213
+ v = ['']
214
+ x = v.append
215
+ else:
216
+ v, x = None, lambda _: None
217
+ if verbosity > 2:
218
+ z, w = [''], ['']
219
+ i = z.append, w.append
220
+ else:
221
+ z = w = None
222
+ i = _(1), _(2)
223
+ if verbosity > 1:
224
+ r = []
225
+ g = r.append
226
+ else:
227
+ r, g = None, _(3)
228
+ while files:
229
+ a = o()
230
+ with _Handler(*i, a) as h:
231
+ q = os.stat(a)
232
+ if h.c:
233
+ continue
234
+ c = h.e
235
+ if q.st_mode & _TYPE_MASK == _S_IFDIR:
236
+ if isinstance(a, int):
237
+ raise _DIRECTORY_DESCRIPTOR_UNSUPPORTED
238
+ x(c)
239
+ u(os.scandir(c))
240
+ continue
241
+ s = q.st_size
242
+ if not s:
243
+ f(c)
244
+ continue
245
+ g((c, s))
246
+ t += s
247
+ if clear:
248
+ h.o()
249
+ del o, files
250
+ while j:
251
+ m = e()
252
+ a = m.path
253
+ if m.is_dir():
254
+ x(a)
255
+ u(os.scandir(a))
256
+ continue
257
+ n += 1
258
+ s = m.stat().st_size
259
+ if not s:
260
+ f(a)
261
+ continue
262
+ g((a, s))
263
+ t += s
264
+ if clear:
265
+ _Handler(*i, a).o()
266
+ x = k[1] if z is None else len(z) - 1
267
+ d = k[2] if w is None else len(w) - 1
268
+ y = k[3] if r is None else len(r)
269
+ if verbosity:
270
+ p = k[0] if b is None else len(b) - 1
271
+ reporter.report(z, w, r, x, y, d, p, v, b, t, n, verbosity, clear)
272
+ reporter.reset_stream()
273
+ return reporter.calculate_result(*map(bool, (y, x and not may_not_exist, d)))
274
+
275
+
276
+ del TYPE_CHECKING
@@ -6,11 +6,8 @@ import check_empty as c
6
6
 
7
7
  TYPE_CHECKING = False
8
8
  if TYPE_CHECKING:
9
- import typing
10
9
  from collections.abc import Iterable
11
-
12
- from check_empty import ExitCode
13
- __all__ = 'main',
10
+ __all__ = ('main',)
14
11
  _p = __import__('argparse').ArgumentParser(
15
12
  'check-empty',
16
13
  description='Assert or enforce that some files, or even directories, are empty. '
@@ -42,7 +39,7 @@ def _r():
42
39
  return _p
43
40
 
44
41
 
45
- def main(argv: Iterable[str] | None = None) -> ExitCode | typing.Literal[2]:
42
+ def main(argv: Iterable[str] | None = None) -> int:
46
43
  """Run the hook/CLI on the files in the command-line arguments passed.
47
44
 
48
45
  Args:
@@ -59,18 +56,13 @@ def main(argv: Iterable[str] | None = None) -> ExitCode | typing.Literal[2]:
59
56
  o = n.out
60
57
  if o is not None:
61
58
  # ruff: ignore[open-file-with-context-handler]
62
- o = open(o, 'w', encoding='utf-8')
63
- try:
64
- return c.check(
65
- n.filenames,
66
- clear=n.clear,
67
- may_not_exist=n.may_not_exist,
68
- verbosity=2 + n.verbose - n.quiet,
69
- out=o,
70
- )
71
- finally:
72
- if o is not None:
73
- o.close()
59
+ c.default_reporter.to(open(o, 'w', encoding='utf-8'))
60
+ return c.check(
61
+ n.filenames,
62
+ clear=n.clear,
63
+ may_not_exist=n.may_not_exist,
64
+ verbosity=2 + n.verbose - n.quiet,
65
+ )
74
66
 
75
67
 
76
68
  del f, TYPE_CHECKING
@@ -1,7 +1,17 @@
1
1
  [build-system]
2
- requires = ["uv_build>=0.12.10"]
2
+ requires = ["uv_build>=0.12.12"]
3
3
  build-backend = "uv_build"
4
4
 
5
+ [dependency-groups]
6
+ docs = [
7
+ "furo==2025.12.19",
8
+ "sphinx==9.1.0",
9
+ "sphinx-argparse-cli==1.22.1",
10
+ "sphinx-copybutton==0.5.2",
11
+ "sphinx-lint==1.0.2",
12
+ "sphinx-notfound-page==1.1.0",
13
+ ]
14
+
5
15
  [project]
6
16
  classifiers = [
7
17
  "Development Status :: 5 - Production/Stable",
@@ -48,7 +58,7 @@ license-files = [
48
58
  name = "check-empty"
49
59
  readme = "README.md"
50
60
  requires-python = ">=3.6"
51
- version = "1.2.2"
61
+ version = "1.2.3"
52
62
 
53
63
  [[project.authors]]
54
64
  name = "Jonathan Dung"
@@ -61,16 +71,6 @@ email = "jonathandung@yahoo.com"
61
71
  [project.scripts]
62
72
  check-empty = "check_empty.__main__:main"
63
73
 
64
- [dependency-groups]
65
- docs = [
66
- "furo==2025.12.19",
67
- "sphinx==9.1.0",
68
- "sphinx-argparse-cli==1.22.1",
69
- "sphinx-copybutton==0.5.2",
70
- "sphinx-lint==1.0.2",
71
- "sphinx-notfound-page==1.1.0",
72
- ]
73
-
74
74
  [tool.ruff]
75
75
  required-version = ">=0.16.6"
76
76
  target-version = "py37"
@@ -129,7 +129,7 @@ all = "error"
129
129
  missing-override-decorator = "ignore"
130
130
 
131
131
  [tool.uv]
132
- required-version = ">=0.12.10"
132
+ required-version = ">=0.12.12"
133
133
 
134
134
  [tool.uv.build-backend]
135
135
  module-name = "check_empty"
@@ -1,7 +1,10 @@
1
1
  [build-system]
2
- requires = ["uv_build>=0.12.10"]
2
+ requires = ["uv_build>=0.12.12"]
3
3
  build-backend = "uv_build"
4
4
 
5
+ [dependency-groups]
6
+ docs = ["furo==2025.12.19", "sphinx==9.1.0", "sphinx-argparse-cli==1.22.1", "sphinx-copybutton==0.5.2", "sphinx-lint==1.0.2", "sphinx-notfound-page==1.1.0"]
7
+
5
8
  [project]
6
9
  authors = [{name = "Jonathan Dung", email = "jonathandung@yahoo.com"}]
7
10
  classifiers = ["Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.15", "Programming Language :: Python :: 3.16", "Programming Language :: Python :: Free Threading :: 3 - Stable", "Topic :: Utilities", "Typing :: Typed"]
@@ -13,10 +16,7 @@ maintainers = [{name = "Jonathan Dung", email = "jonathandung@yahoo.com"}]
13
16
  name = "check-empty"
14
17
  readme = "README.md"
15
18
  requires-python = ">=3.6"
16
- version = "1.2.2"
17
-
18
- [dependency-groups]
19
- docs = ["furo==2025.12.19", "sphinx==9.1.0", "sphinx-argparse-cli==1.22.1", "sphinx-copybutton==0.5.2", "sphinx-lint==1.0.2", "sphinx-notfound-page==1.1.0"]
19
+ version = "1.2.3"
20
20
 
21
21
  [project.scripts]
22
22
  check-empty = "check_empty.__main__:main"
@@ -58,7 +58,7 @@ all = "error"
58
58
  missing-override-decorator = "ignore"
59
59
 
60
60
  [tool.uv]
61
- required-version = ">=0.12.10"
61
+ required-version = ">=0.12.12"
62
62
 
63
63
  [tool.uv.build-backend]
64
64
  module-name = "check_empty"
@@ -1,195 +0,0 @@
1
- # Copyright © 2026 Jonathan Dung. All rights reserved.
2
- # SPDX-License-Identifier: MIT
3
- """Utility to check the emptiness of files and directories.
4
-
5
- Pre-commit hook, command-line tool and GitHub Action all-in-one.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import os
11
-
12
- __all__ = ('ExitCode', 'check')
13
- __version__ = '1.2.2'
14
-
15
- TYPE_CHECKING = False
16
- if TYPE_CHECKING:
17
- import typing
18
- from collections.abc import Iterable
19
-
20
- from _typeshed import FileDescriptorOrPath
21
-
22
- ExitCode: typing.TypeAlias = typing.Literal[0, 1, 4, 5, 8, 9, 12, 13]
23
- else:
24
- ExitCode = int
25
- _DIRECTORY_DESCRIPTOR_UNSUPPORTED: BaseException = (
26
- SystemError('got directory descriptor on Windows')
27
- if os.name == 'nt'
28
- else NotImplementedError('directory descriptors are not supported')
29
- )
30
- _TYPE_MASK: int = 0xF000
31
- _S_IFDIR: int = 0x4000
32
-
33
-
34
- class _Handler:
35
- __slots__ = 'a', 'c', 'f', 'j', 'v'
36
-
37
- def __init__(self, *a):
38
- self.j, self.f, self.a = a
39
- self.v = None
40
-
41
- def __enter__(self):
42
- self.c = False
43
- return self
44
-
45
- def o(self):
46
- with self, open(self.a, 'wb'):
47
- ...
48
-
49
- @property
50
- def e(self, s='invalid fd (negative): %d', t='fd: %d'): # ruff: ignore[property-with-parameters]
51
- r = self.v
52
- if r is None:
53
- a = self.a
54
- self.v = r = (t, s)[a < 0] % a if isinstance(a, int) else os.fsdecode(a)
55
- return r
56
-
57
- def __exit__(self, t, v, _):
58
- if t is None or not issubclass(t, OSError):
59
- return False
60
- self.j(self.e) if v.errno == 2 else self.f(str(v))
61
- self.c = True
62
- return True
63
-
64
-
65
- def check(
66
- files: Iterable[FileDescriptorOrPath],
67
- *,
68
- clear: bool = False,
69
- may_not_exist: bool = False,
70
- verbosity: typing.SupportsIndex = 2,
71
- out: typing.IO[str] | None = None,
72
- ) -> ExitCode:
73
- """Check the emptiness of files, recursing into directories if passed.
74
-
75
- Args:
76
- files: an iterable of file descriptors or paths representing the files and
77
- directories to check; directory descriptors (Unix) are not supported.
78
- clear: if ``True``, clear the contents of non-empty files; directories, notably,
79
- are not purged, but all files within should become empty.
80
- may_not_exist: if ``True``, do not treat absent files or directories as errors.
81
- verbosity: how much detail the program should print to stdout; verbosity > 5 is
82
- equivalent to verbosity = 5, and a non-positive verbosity gives no output.
83
- out: The file to which output is printed; default :data:`sys.stdout`.
84
-
85
- Returns:
86
- The integer exit code. A bitwise or of 1 (some files were not empty), 4 (some
87
- files or directories were absent and :ref:`-m <check-empty--m>` /
88
- :ref:`--may-not-exist <check-empty---may-not-exist>` was omitted) and 8 (caught
89
- :exc:`OSError` while processing some files), such that 0 is correctly the only
90
- return value that represents success. The 2 bit is skipped since 2 is the exit
91
- code of :class:`argparse.ArgumentParser` when it encounters invalid arguments.
92
-
93
- """
94
- files = list(files)
95
- if not files:
96
- return 0
97
- k, j, t, n, o = [0] * 4, [], 0, len(files), files.pop
98
- u, e, _ = j.extend, j.pop, lambda v: lambda _, k=k: k.__setitem__(v, k[v] + 1)
99
-
100
- verbosity = type(verbosity).__index__(verbosity)
101
- if verbosity > 4:
102
- b = ['']
103
- f = b.append
104
- else:
105
- b, f = None, _(0)
106
- if verbosity > 3:
107
- rc = ['']
108
- x = rc.append
109
- else:
110
- rc, x = None, lambda _: None
111
- if verbosity > 2:
112
- z, w = [''], ['']
113
- i = z.append, w.append
114
- else:
115
- z = w = None
116
- i = _(1), _(2)
117
- if verbosity > 1:
118
- r = ['']
119
- g = r.append
120
- else:
121
- r, g = None, _(3)
122
- while files:
123
- a = o()
124
- with _Handler(*i, a) as h:
125
- q = os.stat(a)
126
- if h.c:
127
- continue
128
- c = h.e
129
- if q.st_mode & _TYPE_MASK == _S_IFDIR:
130
- if isinstance(a, int):
131
- raise _DIRECTORY_DESCRIPTOR_UNSUPPORTED
132
- x(c)
133
- u(os.scandir(c))
134
- continue
135
- s = q.st_size
136
- if not s:
137
- f(c)
138
- continue
139
- g(f'{c} ({s} bytes)')
140
- t += s
141
- if clear:
142
- h.o()
143
- del o, files
144
- while j:
145
- m = e()
146
- a = m.path
147
- if m.is_dir():
148
- x(a)
149
- u(os.scandir(a))
150
- continue
151
- n += 1
152
- s = m.stat().st_size
153
- if not s:
154
- f(a)
155
- continue
156
- g(f'{a} ({s} bytes)')
157
- t += s
158
- if clear:
159
- _Handler(*i, a).o()
160
- x = k[1] if z is None else len(z) - 1
161
- y = k[3] if r is None else len(r) - 1
162
- d = k[2] if w is None else len(w) - 1
163
- v = bool(y) | (bool(x) and not may_not_exist) << 2 | bool(d) << 3
164
- if verbosity <= 0:
165
- return v # ty: ignore[invalid-return-type]
166
- q = (__import__('sys').stdout if out is None else out).write
167
- if rc is not None:
168
- q('\nRecursing into directory: '.join(rc))
169
- q('\n\n')
170
- if z is not None:
171
- q('\nNot found: '.join(z))
172
- q('\n')
173
- q(f'{x} file{"s" if x > 1 else ""} not found\n' if x else 'All files were found\n')
174
- if w is not None:
175
- q('\nError: '.join(w))
176
- q('\n')
177
- if d:
178
- q(f'{d} I/O error{"s" if d > 1 else ""} encountered\n')
179
- if b is not None:
180
- q('\nEmpty: '.join(b))
181
- q('\n')
182
- if verbosity > 2:
183
- p = k[0] if b is None else len(b) - 1
184
- q(f'{p or "No"} empty file{"" if p == 1 else "s"}\n')
185
- if y:
186
- if r is not None:
187
- q(('\nCleared: ' if clear else '\nNot empty: ').join(r))
188
- q('\n\n')
189
- q(f'{y} offending file{"s" if y > 1 else ""}\nTotal size: {t} bytes\n')
190
- elif n > x:
191
- q('All found files were empty\n')
192
- return v # ty: ignore[invalid-return-type]
193
-
194
-
195
- del TYPE_CHECKING
File without changes
File without changes