alissa-tools-github-revloop 0.31.25__tar.gz → 0.31.26__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.
- {alissa_tools_github_revloop-0.31.25/src/main/alissa_tools_github_revloop.egg-info → alissa_tools_github_revloop-0.31.26}/PKG-INFO +1 -1
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/janitor.py +90 -24
- alissa_tools_github_revloop-0.31.26/src/main/alissa/tools/github/revloop/version +1 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26/src/main/alissa_tools_github_revloop.egg-info}/PKG-INFO +1 -1
- alissa_tools_github_revloop-0.31.25/src/main/alissa/tools/github/revloop/version +0 -1
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/LICENSE +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/MANIFEST.in +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/NOTICE +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/README.md +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/requirements.txt +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/setup.cfg +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/setup.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/__main__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/alissa.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/alissa_client.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/bows.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/config.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/fleet_vitals.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/ghclient.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/loop.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/loop_events.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/parking.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/proc.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/prompts.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/prreview.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/state.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/trust.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/version.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/sources.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa_tools_github_revloop.egg-info/SOURCES.txt +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa_tools_github_revloop.egg-info/dependency_links.txt +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa_tools_github_revloop.egg-info/entry_points.txt +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/src/main/alissa_tools_github_revloop.egg-info/top_level.txt +0 -0
|
@@ -124,9 +124,26 @@ BACKSTOP_PCT = 95.0
|
|
|
124
124
|
# or not.
|
|
125
125
|
SIZE_CACHE_SECONDS = 60 * 60
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class HeldWalk:
|
|
130
|
+
"""A held folder's size walk that ran out of budget (issue #189), kept
|
|
131
|
+
so the next sweep resumes it instead of starting over: the folder's
|
|
132
|
+
newest mtime when the walk began, the sweep `now` it began at, the bytes
|
|
133
|
+
counted so far, and the directories still to read. A resumed walk's
|
|
134
|
+
total may count a directory removed since it was read; it never counts a
|
|
135
|
+
directory twice."""
|
|
136
|
+
|
|
137
|
+
mtime: float
|
|
138
|
+
started: float
|
|
139
|
+
total: int
|
|
140
|
+
stack: "list[str]"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# One held folder's cached size, by path: either a finished walk, as
|
|
144
|
+
# (newest mtime, bytes, the sweep `now` the walk began at), or a `HeldWalk`
|
|
145
|
+
# that the time budget cut short (issue #189).
|
|
146
|
+
SizeCache = dict[Path, "tuple[float, int, float] | HeldWalk"]
|
|
130
147
|
|
|
131
148
|
# The two reasons a hub-wide hold gives, so the sweep summary groups them.
|
|
132
149
|
HUB_HOLD_BOUNDED = "a live round in this hub names no review task and spawned before this checkout"
|
|
@@ -274,6 +291,9 @@ class SweepResult:
|
|
|
274
291
|
# this sweep, so the summary can say its figure may be up to
|
|
275
292
|
# SIZE_CACHE_SECONDS old.
|
|
276
293
|
held_cached: int = 0
|
|
294
|
+
# Issue #189: unsized held folders whose walk is kept to resume next
|
|
295
|
+
# sweep (a subset of `held_unsized`).
|
|
296
|
+
held_resuming: int = 0
|
|
277
297
|
backstop: bool = False
|
|
278
298
|
backstop_released: int = 0
|
|
279
299
|
|
|
@@ -321,6 +341,8 @@ def held_summary(result: SweepResult) -> str:
|
|
|
321
341
|
)
|
|
322
342
|
if result.held_unsized:
|
|
323
343
|
tail += f"; {result.held_unsized} held folder(s) not sized this sweep"
|
|
344
|
+
if result.held_resuming:
|
|
345
|
+
tail += f" ({result.held_resuming} walk(s) resume next sweep)"
|
|
324
346
|
return tail
|
|
325
347
|
|
|
326
348
|
|
|
@@ -438,19 +460,31 @@ def dir_bytes(path: "Path | str") -> int:
|
|
|
438
460
|
return _walk_bytes(path, None) or 0
|
|
439
461
|
|
|
440
462
|
|
|
441
|
-
def held_bytes_of(
|
|
463
|
+
def held_bytes_of(
|
|
464
|
+
path: "Path | str",
|
|
465
|
+
stop: "Callable[[], bool]",
|
|
466
|
+
progress: "HeldWalk | None" = None,
|
|
467
|
+
) -> "int | None":
|
|
442
468
|
"""`dir_bytes` for a held folder (issue #187): `stop` is asked before
|
|
443
469
|
each directory is read, and the walk gives up -- None, never a partial
|
|
444
470
|
figure -- the moment it answers True, so one large tree cannot run the
|
|
445
|
-
sweep past its time budget.
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
471
|
+
sweep past its time budget. With `progress` (issue #189) the walk starts
|
|
472
|
+
from its `total` and `stack` rather than from `path`, and an abandoned
|
|
473
|
+
walk leaves its place there, so a later call carries on from it."""
|
|
474
|
+
return _walk_bytes(path, stop, progress)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _walk_bytes(
|
|
478
|
+
path: "Path | str",
|
|
479
|
+
stop: "Callable[[], bool] | None",
|
|
480
|
+
progress: "HeldWalk | None" = None,
|
|
481
|
+
) -> "int | None":
|
|
482
|
+
total = progress.total if progress is not None else 0
|
|
483
|
+
stack = progress.stack if progress is not None else [os.fspath(path)]
|
|
452
484
|
while stack:
|
|
453
485
|
if stop is not None and stop():
|
|
486
|
+
if progress is not None:
|
|
487
|
+
progress.total = total # `stack` is progress.stack, in place
|
|
454
488
|
return None
|
|
455
489
|
current = stack.pop()
|
|
456
490
|
try:
|
|
@@ -600,7 +634,11 @@ def sweep(
|
|
|
600
634
|
`held_unsized`. `sizes` (a `SizeCache`), when given, caches those walks
|
|
601
635
|
across sweeps and is re-keyed to this sweep's held folders; an entry is
|
|
602
636
|
reused while the folder's mtime is unchanged AND it is younger than
|
|
603
|
-
SIZE_CACHE_SECONDS by `now`, and each reuse is counted in `held_cached`.
|
|
637
|
+
SIZE_CACHE_SECONDS by `now`, and each reuse is counted in `held_cached`.
|
|
638
|
+
An abandoned walk is kept there too (issue #189): the next sweep sizes
|
|
639
|
+
every other held folder first and then resumes it where it stopped, so
|
|
640
|
+
one large tree neither starves the folders behind it nor restarts from
|
|
641
|
+
scratch every sweep."""
|
|
604
642
|
result = SweepResult()
|
|
605
643
|
started = clock()
|
|
606
644
|
bounded = False
|
|
@@ -702,24 +740,52 @@ def _size_held(
|
|
|
702
740
|
"""Size the sweep's held folders into `result.held_bytes`, after the
|
|
703
741
|
removals (issue #187). A fresh cache entry costs nothing and is always
|
|
704
742
|
used; anything else is walked only while `out_of_time()` answers False,
|
|
705
|
-
and a walk is abandoned the moment it does.
|
|
706
|
-
|
|
743
|
+
and a walk is abandoned the moment it does.
|
|
744
|
+
|
|
745
|
+
An abandoned walk is kept in `sizes` as a `HeldWalk` (issue #189). Next
|
|
746
|
+
sweep its folder goes to the back of the queue, behind every folder
|
|
747
|
+
whose walk was not cut short, and its walk resumes from where it stopped
|
|
748
|
+
-- from scratch if the folder's mtime moved. So a tree too big for one
|
|
749
|
+
sweep's leftover budget no longer leaves the folders behind it unsized
|
|
750
|
+
on every sweep, and still gets its figure after a few. A finished
|
|
751
|
+
resumed walk is cached as of the sweep it began in, so the hourly
|
|
752
|
+
re-walk counts from the oldest directory it read.
|
|
753
|
+
|
|
754
|
+
`sizes` is then re-keyed to this sweep's held folders: entries for gone
|
|
755
|
+
folders drop out, and so do unsized folders whose walk never began."""
|
|
756
|
+
previous = sizes or {}
|
|
757
|
+
|
|
758
|
+
def cut_short(item: "tuple[float, Candidate, str]") -> bool:
|
|
759
|
+
return isinstance(previous.get(item[1].path), HeldWalk)
|
|
760
|
+
|
|
707
761
|
seen: SizeCache = {}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
762
|
+
# Stable: oldest first within each group, as the sweep queued them.
|
|
763
|
+
for mtime, candidate, key in sorted(queue, key=cut_short):
|
|
764
|
+
cached = previous.get(candidate.path)
|
|
765
|
+
if (isinstance(cached, tuple) and cached[0] == mtime
|
|
711
766
|
and now - cached[2] < SIZE_CACHE_SECONDS):
|
|
712
767
|
size = cached[1]
|
|
713
768
|
seen[candidate.path] = cached
|
|
714
769
|
result.held_cached += 1
|
|
770
|
+
result.held_bytes[key] = result.held_bytes.get(key, 0) + size
|
|
771
|
+
continue
|
|
772
|
+
resumable = isinstance(cached, HeldWalk)
|
|
773
|
+
if isinstance(cached, HeldWalk) and cached.mtime == mtime:
|
|
774
|
+
progress = cached
|
|
715
775
|
else:
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
776
|
+
progress = HeldWalk(mtime, now, 0, [os.fspath(candidate.path)])
|
|
777
|
+
walked = None
|
|
778
|
+
if not out_of_time():
|
|
779
|
+
walked = held_bytes_of(candidate.path, out_of_time, progress)
|
|
780
|
+
resumable = resumable or walked is None
|
|
781
|
+
if walked is None:
|
|
782
|
+
result.held_unsized += 1
|
|
783
|
+
if resumable:
|
|
784
|
+
seen[candidate.path] = progress
|
|
785
|
+
result.held_resuming += 1
|
|
786
|
+
continue
|
|
787
|
+
seen[candidate.path] = (mtime, walked, progress.started)
|
|
788
|
+
result.held_bytes[key] = result.held_bytes.get(key, 0) + walked
|
|
723
789
|
if sizes is not None:
|
|
724
790
|
sizes.clear()
|
|
725
791
|
sizes.update(seen)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.31.26
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.31.25
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.26}/requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|