alissa-tools-github-revloop 0.31.25__tar.gz → 0.31.27__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.27}/PKG-INFO +1 -1
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/janitor.py +108 -24
- alissa_tools_github_revloop-0.31.27/src/main/alissa/tools/github/revloop/version +1 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27/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.27}/LICENSE +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/MANIFEST.in +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/NOTICE +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/README.md +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/requirements.txt +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/setup.cfg +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/setup.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/__main__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/alissa.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/alissa_client.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/bows.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/config.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/fleet_vitals.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/ghclient.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/loop.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/loop_events.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/parking.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/proc.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/prompts.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/prreview.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/state.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/trust.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/version.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/sources.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +0 -0
- {alissa_tools_github_revloop-0.31.25 → alissa_tools_github_revloop-0.31.27}/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.27}/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.27}/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.27}/src/main/alissa_tools_github_revloop.egg-info/top_level.txt +0 -0
|
@@ -124,9 +124,32 @@ 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
|
+
`advanced` is the sweep `now` the walk last read a directory (issue
|
|
138
|
+
#191), 0 until it has. Kept walks resume least-recently-advanced first,
|
|
139
|
+
so two walks that each need several sweeps take turns: the one that just
|
|
140
|
+
had the leftover budget waits behind the one that did not."""
|
|
141
|
+
|
|
142
|
+
mtime: float
|
|
143
|
+
started: float
|
|
144
|
+
total: int
|
|
145
|
+
stack: "list[str]"
|
|
146
|
+
advanced: float = 0.0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# One held folder's cached size, by path: either a finished walk, as
|
|
150
|
+
# (newest mtime, bytes, the sweep `now` the walk began at), or a `HeldWalk`
|
|
151
|
+
# that the time budget cut short (issue #189).
|
|
152
|
+
SizeCache = dict[Path, "tuple[float, int, float] | HeldWalk"]
|
|
130
153
|
|
|
131
154
|
# The two reasons a hub-wide hold gives, so the sweep summary groups them.
|
|
132
155
|
HUB_HOLD_BOUNDED = "a live round in this hub names no review task and spawned before this checkout"
|
|
@@ -274,6 +297,9 @@ class SweepResult:
|
|
|
274
297
|
# this sweep, so the summary can say its figure may be up to
|
|
275
298
|
# SIZE_CACHE_SECONDS old.
|
|
276
299
|
held_cached: int = 0
|
|
300
|
+
# Issue #189: unsized held folders whose walk is kept to resume next
|
|
301
|
+
# sweep (a subset of `held_unsized`).
|
|
302
|
+
held_resuming: int = 0
|
|
277
303
|
backstop: bool = False
|
|
278
304
|
backstop_released: int = 0
|
|
279
305
|
|
|
@@ -321,6 +347,8 @@ def held_summary(result: SweepResult) -> str:
|
|
|
321
347
|
)
|
|
322
348
|
if result.held_unsized:
|
|
323
349
|
tail += f"; {result.held_unsized} held folder(s) not sized this sweep"
|
|
350
|
+
if result.held_resuming:
|
|
351
|
+
tail += f" ({result.held_resuming} walk(s) resume next sweep)"
|
|
324
352
|
return tail
|
|
325
353
|
|
|
326
354
|
|
|
@@ -438,19 +466,31 @@ def dir_bytes(path: "Path | str") -> int:
|
|
|
438
466
|
return _walk_bytes(path, None) or 0
|
|
439
467
|
|
|
440
468
|
|
|
441
|
-
def held_bytes_of(
|
|
469
|
+
def held_bytes_of(
|
|
470
|
+
path: "Path | str",
|
|
471
|
+
stop: "Callable[[], bool]",
|
|
472
|
+
progress: "HeldWalk | None" = None,
|
|
473
|
+
) -> "int | None":
|
|
442
474
|
"""`dir_bytes` for a held folder (issue #187): `stop` is asked before
|
|
443
475
|
each directory is read, and the walk gives up -- None, never a partial
|
|
444
476
|
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
|
-
|
|
477
|
+
sweep past its time budget. With `progress` (issue #189) the walk starts
|
|
478
|
+
from its `total` and `stack` rather than from `path`, and an abandoned
|
|
479
|
+
walk leaves its place there, so a later call carries on from it."""
|
|
480
|
+
return _walk_bytes(path, stop, progress)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _walk_bytes(
|
|
484
|
+
path: "Path | str",
|
|
485
|
+
stop: "Callable[[], bool] | None",
|
|
486
|
+
progress: "HeldWalk | None" = None,
|
|
487
|
+
) -> "int | None":
|
|
488
|
+
total = progress.total if progress is not None else 0
|
|
489
|
+
stack = progress.stack if progress is not None else [os.fspath(path)]
|
|
452
490
|
while stack:
|
|
453
491
|
if stop is not None and stop():
|
|
492
|
+
if progress is not None:
|
|
493
|
+
progress.total = total # `stack` is progress.stack, in place
|
|
454
494
|
return None
|
|
455
495
|
current = stack.pop()
|
|
456
496
|
try:
|
|
@@ -600,7 +640,11 @@ def sweep(
|
|
|
600
640
|
`held_unsized`. `sizes` (a `SizeCache`), when given, caches those walks
|
|
601
641
|
across sweeps and is re-keyed to this sweep's held folders; an entry is
|
|
602
642
|
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`.
|
|
643
|
+
SIZE_CACHE_SECONDS by `now`, and each reuse is counted in `held_cached`.
|
|
644
|
+
An abandoned walk is kept there too (issue #189): the next sweep sizes
|
|
645
|
+
every other held folder first and then resumes it where it stopped, so
|
|
646
|
+
one large tree neither starves the folders behind it nor restarts from
|
|
647
|
+
scratch every sweep."""
|
|
604
648
|
result = SweepResult()
|
|
605
649
|
started = clock()
|
|
606
650
|
bounded = False
|
|
@@ -702,24 +746,64 @@ def _size_held(
|
|
|
702
746
|
"""Size the sweep's held folders into `result.held_bytes`, after the
|
|
703
747
|
removals (issue #187). A fresh cache entry costs nothing and is always
|
|
704
748
|
used; anything else is walked only while `out_of_time()` answers False,
|
|
705
|
-
and a walk is abandoned the moment it does.
|
|
706
|
-
|
|
749
|
+
and a walk is abandoned the moment it does.
|
|
750
|
+
|
|
751
|
+
An abandoned walk is kept in `sizes` as a `HeldWalk` (issue #189). Next
|
|
752
|
+
sweep its folder goes to the back of the queue, behind every folder
|
|
753
|
+
whose walk was not cut short, and its walk resumes from where it stopped
|
|
754
|
+
-- from scratch if the folder's mtime moved. So a tree too big for one
|
|
755
|
+
sweep's leftover budget no longer leaves the folders behind it unsized
|
|
756
|
+
on every sweep, and still gets its figure after a few. A finished
|
|
757
|
+
resumed walk is cached as of the sweep it began in, so the hourly
|
|
758
|
+
re-walk counts from the oldest directory it read.
|
|
759
|
+
|
|
760
|
+
Kept walks go least-recently-advanced first, not oldest first (issue
|
|
761
|
+
#191). Otherwise a walk that needs more than SIZE_CACHE_SECONDS of
|
|
762
|
+
sweeps -- whose figure has expired by the sweep after it lands, so it
|
|
763
|
+
starts over -- takes every sweep's leftover budget, and a younger kept
|
|
764
|
+
walk never reads a directory.
|
|
765
|
+
|
|
766
|
+
`sizes` is then re-keyed to this sweep's held folders: entries for gone
|
|
767
|
+
folders drop out, and so do unsized folders whose walk never began."""
|
|
768
|
+
previous = sizes or {}
|
|
769
|
+
|
|
770
|
+
def turn(item: "tuple[float, Candidate, str]") -> "tuple[bool, float]":
|
|
771
|
+
kept = previous.get(item[1].path)
|
|
772
|
+
if isinstance(kept, HeldWalk):
|
|
773
|
+
return True, kept.advanced
|
|
774
|
+
return False, 0.0
|
|
775
|
+
|
|
707
776
|
seen: SizeCache = {}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
777
|
+
# Stable: oldest first among the folders not cut short and among kept
|
|
778
|
+
# walks that last advanced in the same sweep, as the sweep queued them.
|
|
779
|
+
for mtime, candidate, key in sorted(queue, key=turn):
|
|
780
|
+
cached = previous.get(candidate.path)
|
|
781
|
+
if (isinstance(cached, tuple) and cached[0] == mtime
|
|
711
782
|
and now - cached[2] < SIZE_CACHE_SECONDS):
|
|
712
783
|
size = cached[1]
|
|
713
784
|
seen[candidate.path] = cached
|
|
714
785
|
result.held_cached += 1
|
|
786
|
+
result.held_bytes[key] = result.held_bytes.get(key, 0) + size
|
|
787
|
+
continue
|
|
788
|
+
resumable = isinstance(cached, HeldWalk)
|
|
789
|
+
if isinstance(cached, HeldWalk) and cached.mtime == mtime:
|
|
790
|
+
progress = cached
|
|
715
791
|
else:
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
792
|
+
progress = HeldWalk(mtime, now, 0, [os.fspath(candidate.path)],
|
|
793
|
+
cached.advanced if isinstance(cached, HeldWalk) else 0.0)
|
|
794
|
+
walked = None
|
|
795
|
+
if not out_of_time():
|
|
796
|
+
progress.advanced = now
|
|
797
|
+
walked = held_bytes_of(candidate.path, out_of_time, progress)
|
|
798
|
+
resumable = resumable or walked is None
|
|
799
|
+
if walked is None:
|
|
800
|
+
result.held_unsized += 1
|
|
801
|
+
if resumable:
|
|
802
|
+
seen[candidate.path] = progress
|
|
803
|
+
result.held_resuming += 1
|
|
804
|
+
continue
|
|
805
|
+
seen[candidate.path] = (mtime, walked, progress.started)
|
|
806
|
+
result.held_bytes[key] = result.held_bytes.get(key, 0) + walked
|
|
723
807
|
if sizes is not None:
|
|
724
808
|
sizes.clear()
|
|
725
809
|
sizes.update(seen)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.31.27
|
|
@@ -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.27}/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
|