delayed-rm 2.4.1__tar.gz → 2.7.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.
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: delayed_rm
3
- Version: 2.4.1
3
+ Version: 2.7.0
4
4
  Summary: Ever wish you had a few minutes to undo an rm? Now you do!
5
5
  Home-page: https://github.com/zwimer/delayed_rm
6
6
  License: GPL
7
+ Project-URL: Homepage, https://github.com/zwimer/delayed_rm
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: Programming Language :: Python :: 3.8
9
10
  Requires-Python: >=3.8
@@ -11,7 +12,7 @@ Description-Content-Type: text/markdown
11
12
  License-File: LICENSE
12
13
 
13
14
  # delayed\_rm
14
- Ever wish you had a few minutes to undo an rm? Now you do!
15
+ Ever wish you had a few minutes to undo an rm? Now you do!
15
16
 
16
17
  # Usage
17
18
 
@@ -1,5 +1,5 @@
1
1
  # delayed\_rm
2
- Ever wish you had a few minutes to undo an rm? Now you do!
2
+ Ever wish you had a few minutes to undo an rm? Now you do!
3
3
 
4
4
  # Usage
5
5
 
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
  from collections import defaultdict
3
- from typing import Callable, Set
4
3
  from tempfile import gettempdir
5
4
  from datetime import datetime
5
+ from typing import Callable
6
6
  from pathlib import Path
7
7
  import subprocess
8
8
  import argparse
@@ -13,7 +13,7 @@ import sys
13
13
  import os
14
14
 
15
15
 
16
- __version__ = "2.4.1"
16
+ __version__ = "2.7.0"
17
17
 
18
18
 
19
19
  #
@@ -42,6 +42,7 @@ class _Secret:
42
42
  Contains a string needed to activate the secret CLI
43
43
  Users should *not* use this, it is an internal class!
44
44
  """
45
+
45
46
  key: str = "DELAYED_RM_SECRET_CLI"
46
47
  value: str = "cL5r0!L4hmWmonW7k^RZM*4nq7mR&yfF"
47
48
 
@@ -58,8 +59,11 @@ def _eprint(e: str | BaseException) -> None:
58
59
  e2: str | BaseException = e
59
60
  if isinstance(e, RMError) and isinstance(e.__cause__, BaseException):
60
61
  e2 = e.__cause__
61
- err: str = "Error" if isinstance(e2, RMError) or not isinstance(e, BaseException) else \
62
- str(type(e2)).split("'")[1].split("delayed_rm.")[-1]
62
+ err: str = (
63
+ "Error"
64
+ if isinstance(e2, RMError) or not isinstance(e, BaseException)
65
+ else str(type(e2)).split("'")[1].split("delayed_rm.")[-1]
66
+ )
63
67
  print(f"{err}: {e}", file=sys.stderr)
64
68
 
65
69
 
@@ -79,7 +83,7 @@ def _prep(paths: list[Path], rf: bool) -> list[Path]:
79
83
  """
80
84
  # Normalize paths and error checking
81
85
  try:
82
- paths = [ i.parent.resolve(strict=True) / i.name for i in paths ]
86
+ paths = [i.parent.resolve(strict=True) / i.name for i in paths]
83
87
  # pathlib.stat does not support follow_symlinks until 3.10
84
88
  if len(paths) != len({os.stat(i, follow_symlinks=False).st_ino for i in paths}):
85
89
  raise RMError("duplicate items passed")
@@ -120,64 +124,82 @@ def delayed_rm(paths: list[Path], delay: int, rf: bool) -> bool:
120
124
  # Init data structures
121
125
  success: list[Path] = []
122
126
  failed: list[Path] = []
123
- out_dirs: Set[Path] = { _mkdir(base / "0") }
124
- where: dict[str, Set[Path]] = defaultdict(set)
127
+ out_dirs: set[Path] = {_mkdir(base / "0")}
128
+ where: dict[str, set[Path]] = defaultdict(set)
125
129
  full_where: dict[Path, Path] = {}
126
130
  # Delete files
127
- for p in paths:
128
- # Select an output directory that an item of name p.name does not exist
129
- outd: Path
130
- if len(out_dirs) != len(where[p.name]):
131
- outd = next(iter(out_dirs - where[p.name]))
132
- else:
133
- outd = _mkdir(base / str(len(out_dirs)))
134
- out_dirs.add(outd)
135
- # Move file into the temp directory
136
- try:
137
- new: Path = outd / p.name
131
+ ctrlc = False
132
+ edited = False
133
+ try:
134
+ for p in paths:
135
+ # Select an output directory that an item of name p.name does not exist
136
+ outd: Path
137
+ if len(out_dirs) != len(where[p.name]):
138
+ outd = next(iter(out_dirs - where[p.name]))
139
+ else:
140
+ outd = _mkdir(base / str(len(out_dirs)))
141
+ out_dirs.add(outd)
142
+ # Move file into the temp directory
138
143
  try:
139
- p.rename(new)
140
- except OSError:
141
- copyf = lambda src, dst: shutil.copy2(src, dst, follow_symlinks=False)
142
- if p.is_dir():
143
- shutil.copytree(p, new, copy_function=copyf, symlinks=False)
144
- shutil.rmtree(p)
145
- else:
146
- copyf(p, new)
147
- p.unlink()
148
- full_where[p] = new
149
- where[p.name].add(outd)
150
- success.append(p)
151
- except OSError as e:
152
- failed.append(p)
153
- _eprint(e)
144
+ new: Path = outd / p.name
145
+ try:
146
+ p.rename(new)
147
+ except OSError:
148
+ copyf = lambda src, dst: shutil.copy2(src, dst, follow_symlinks=False)
149
+ if p.is_dir():
150
+ shutil.copytree(p, new, copy_function=copyf, symlinks=False)
151
+ edited = True
152
+ shutil.rmtree(p)
153
+ else:
154
+ copyf(p, new)
155
+ edited = True
156
+ p.unlink()
157
+ success.append(p)
158
+ full_where[p] = new
159
+ where[p.name].add(outd)
160
+ except OSError as e:
161
+ failed.append(p)
162
+ _eprint(e)
163
+ except KeyboardInterrupt:
164
+ ctrlc = True
154
165
  # Inform user of failures
155
166
  failed_plus: list[str] = [str(i) for i in failed]
156
- if len(failed) > 0:
167
+ if len(failed) > 0 and not ctrlc:
157
168
  _eprint("failed to rm:\n " + "\n ".join(failed_plus))
158
169
  # Log result
159
170
  success_plus: list[str] = [f"{i} ---> {full_where[i]}" for i in success]
160
171
  fmt: Callable[[list[str]], str] = lambda l: ("\n " + "\n ".join(l)) if l else " None"
161
- msg: str = str(datetime.now()) + "\n " + "\n".join((
162
- f"Delay: {delay}",
163
- f"rf: {rf}",
164
- f"Storage Directory: {base}",
165
- f"Succeeded:{fmt(success_plus)}",
166
- f"Failed:{fmt(failed_plus)}",
167
- )).replace("\n", "\n ") + "\n\n"
168
- with log_f.open("a") as f:
169
- f.write(msg)
172
+ msg: str = (
173
+ str(datetime.now())
174
+ + "\n "
175
+ + "\n".join(
176
+ (
177
+ ("Interrupted by: SIGINT\n" if ctrlc else "") + f"Delay: {delay}",
178
+ f"rf: {rf}",
179
+ f"Storage Directory: {base}",
180
+ f"Succeeded:{fmt(success_plus)}",
181
+ f"Failed:{fmt(failed_plus)}",
182
+ )
183
+ ).replace("\n", "\n ")
184
+ + "\n\n"
185
+ )
186
+ try:
187
+ with log_f.open("a") as f:
188
+ f.write(msg)
189
+ except OSError:
190
+ print(msg)
191
+ raise
170
192
  # Delay rm and die
171
- if not success:
193
+ if not edited:
172
194
  shutil.rmtree(base)
173
195
  else:
174
196
  subprocess.Popen( # pylint: disable=consider-using-with
175
197
  (sys.executable, __file__, _Secret.value, str(delay), base),
176
- env = { _Secret.key: _Secret.value },
198
+ env={_Secret.key: _Secret.value},
177
199
  stdout=subprocess.DEVNULL,
178
200
  stderr=subprocess.DEVNULL,
179
201
  )
180
- return not failed
202
+ return not failed and not ctrlc
181
203
 
182
204
 
183
205
  def delayed_rm_raw(delay: int, log: bool, r: bool, f: bool, paths: list[Path]) -> bool:
@@ -216,7 +238,9 @@ def main(prog: str, *args: str) -> bool:
216
238
  parser = argparse.ArgumentParser(prog=base)
217
239
  parser.add_argument("--version", action="version", version=f"{base} {__version__}")
218
240
  parser.add_argument("-d", "--delay", type=int, default=900, help="The deletion delay in seconds")
219
- parser.add_argument("--log", action="store_true", help=f"Show {base}'s log files; may not be used with other arguments")
241
+ parser.add_argument(
242
+ "--log", action="store_true", help=f"Show {base}'s log files; may not be used with other arguments"
243
+ )
220
244
  parser.add_argument("-r", action="store_true", help="rm -r; must use -f with this")
221
245
  parser.add_argument("-f", action="store_true", help="rm -f; must use -r with this")
222
246
  parser.add_argument("paths", type=Path, nargs="*", help="The items to delete")
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.1
2
- Name: delayed-rm
3
- Version: 2.4.1
2
+ Name: delayed_rm
3
+ Version: 2.7.0
4
4
  Summary: Ever wish you had a few minutes to undo an rm? Now you do!
5
5
  Home-page: https://github.com/zwimer/delayed_rm
6
6
  License: GPL
7
+ Project-URL: Homepage, https://github.com/zwimer/delayed_rm
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: Programming Language :: Python :: 3.8
9
10
  Requires-Python: >=3.8
@@ -11,7 +12,7 @@ Description-Content-Type: text/markdown
11
12
  License-File: LICENSE
12
13
 
13
14
  # delayed\_rm
14
- Ever wish you had a few minutes to undo an rm? Now you do!
15
+ Ever wish you had a few minutes to undo an rm? Now you do!
15
16
 
16
17
  # Usage
17
18
 
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "delayed_rm"
7
+ classifiers = [
8
+ "Programming Language :: Python :: 3",
9
+ "Programming Language :: Python :: 3.8",
10
+ ]
11
+ license = {text = "GPL"}
12
+ description = "Ever wish you had a few minutes to undo an rm? Now you do!"
13
+ urls = {Homepage = "https://github.com/zwimer/delayed_rm"}
14
+ requires-python = ">= 3.8"
15
+ dynamic = ["version"]
16
+
17
+ [project.readme]
18
+ file = "README.md"
19
+ content-type = "text/markdown"
20
+
21
+ [project.scripts]
22
+ delayed_rm = "delayed_rm:cli"
23
+
24
+ [tool.setuptools]
25
+ license-files = ["LICENSE"]
26
+ include-package-data = false
27
+
28
+ [tool.setuptools.packages]
29
+ find = {namespaces = false}
30
+
31
+ [tool.setuptools.package-data]
32
+ delayed_rm = ["py.typed"]
33
+
34
+ [tool.setuptools.dynamic]
35
+ version = {attr = "delayed_rm.__version__"}
36
+
37
+ # Tools
38
+
39
+ [tool.black]
40
+ line-length = 120
41
+ target-version = ["py38"]
42
+
43
+ [tool.ruff]
44
+ ignore=["E731","E741"]
45
+ line-length = 120
46
+ [tool.ruff.per-file-ignores]
47
+ "__init__.py" = ["F401", "F403"]
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=46.4.0"]
3
- build-backend = "setuptools.build_meta"
File without changes
File without changes