99import tempfile
1010import textwrap
1111from collections import UserDict , defaultdict , deque
12+ from collections .abc import Iterable
1213from dataclasses import dataclass
1314from enum import Enum
1415from functools import partial
@@ -292,35 +293,26 @@ def __call__(self, parser, namespace, values, option_string=None):
292293class HistoricalRepo (UnconfiguredTree ):
293294 """Repository of historical packages stored in a temporary directory."""
294295
295- def __init__ (self , parent_repo , tmpdir , * args , ** kwargs ):
296- self .__parent_repo = parent_repo
296+ def __init__ (self , tmpdir , * args , ** kwargs ):
297297 # cache tmpdir to delay cleanup until exit
298298 self .__tmpdir = tmpdir
299299 super ().__init__ (* args , ** kwargs )
300300
301- def add_pkgs (self , pkgs ):
302- """Update the repo with a given sequence of packages."""
303- self ._populate (pkgs )
304- # notify the repo object that new pkgs were added
305- for pkg in pkgs :
306- self .notify_add_package (pkg )
307-
308- def _populate (self , pkgs ):
309- """Populate the repo with a given sequence of historical packages."""
310- paths = {pkg .key for pkg in pkgs }
311- old_files = subprocess .Popen (
312- ["git" , "archive" , "HEAD" ] + list (paths ),
313- stdout = subprocess .PIPE ,
314- stderr = subprocess .PIPE ,
315- cwd = self .__parent_repo .location ,
316- )
317- if old_files .poll (): # pragma: no cover
318- error = old_files .stderr .read ().decode ().strip ()
319- raise Exception (f"failed populating archive repo: { error } " )
320- with tarfile .open (mode = "r|" , fileobj = old_files .stdout ) as tar :
321- # see filter in https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
322- # Whilst we trust git archive, we still leave the basic protections on.
323- tar .extractall (path = self .location , filter = "data" )
301+
302+ def _extract_historical (repo , path : str , keys : Iterable [str ]):
303+ """Extract the packages as they are in HEAD into a directory."""
304+ old_files = subprocess .Popen (
305+ ["git" , "archive" , "HEAD" , * keys ],
306+ stdout = subprocess .PIPE ,
307+ stderr = subprocess .PIPE ,
308+ cwd = repo .location ,
309+ )
310+ if old_files .poll (): # pragma: no cover
311+ error = old_files .stderr .read ().decode ().strip ()
312+ raise RuntimeError (f"failed populating archive repo: { error } " )
313+ with tarfile .open (mode = "r|" , fileobj = old_files .stdout ) as tar :
314+ # Whilst we trust git archive, we still leave the basic protections on.
315+ tar .extractall (path = path , filter = "data" )
324316
325317
326318# GLEP 81 categories, mapped to what their packages allocate
@@ -349,13 +341,14 @@ class ChangeSummary:
349341 # mapping of handled statuses to functions
350342 status_funcs = None
351343
352- def __init__ (self , options ):
344+ def __init__ (self , options , changes : "Iterable[PkgChange]" ):
353345 self .options = options
354346 self .repo = options .repo
347+ self .changes = {x .atom : x for x in changes }
355348
356349 @jit_attr
357350 def old_repo (self ):
358- """Create a repository of historical packages removed from git ."""
351+ """Create a repository of the changed packages as they are in HEAD ."""
359352 tmpdir = tempfile .TemporaryDirectory ()
360353 repo_dir = tmpdir .name
361354
@@ -367,8 +360,10 @@ def old_repo(self):
367360 with open (pjoin (repo_dir , "profiles" , "repo_name" ), "w" ) as f :
368361 f .write (f"{ self .repo .repo_id } -old\n " )
369362
363+ _extract_historical (self .repo , repo_dir , {atom .key for atom in self .changes })
364+
370365 repo_config = RepoConfig (repo_dir )
371- tree_cls = partial (HistoricalRepo , self . repo , tmpdir )
366+ tree_cls = partial (HistoricalRepo , tmpdir )
372367 return tree (self .options .config , repo_config , tree_cls = tree_cls )
373368
374369 def __str__ (self ):
@@ -387,16 +382,11 @@ class MetadataSummary(ChangeSummary):
387382
388383 status_funcs : ClassVar [dict [str , callable ]] = {}
389384
390- def __init__ (self , options , changes ):
391- super ().__init__ (options )
392- self .changes = {x .atom : x for x in changes }
393-
394385 @change ("M" )
395386 def modify (self ):
396387 """Generate summaries for modify actions."""
397388 atom = next (iter (self .changes ))
398389 pkgs = self .repo .match (atom )
399- self .old_repo .add_pkgs (pkgs )
400390 try :
401391 old_pkg = self .old_repo .match (atom )[0 ]
402392 new_pkg = pkgs [0 ]
@@ -443,10 +433,6 @@ class PkgSummary(ChangeSummary):
443433
444434 status_funcs : ClassVar [dict [str , callable ]] = {}
445435
446- def __init__ (self , options , changes ):
447- super ().__init__ (options )
448- self .changes = {x .atom : x for x in changes }
449-
450436 @jit_attr
451437 def versions (self ):
452438 """Tuple of package versions that were changed."""
@@ -535,7 +521,6 @@ def modify(self):
535521 summaries = set ()
536522 for atom in self .changes :
537523 pkgs = self .repo .match (atom )
538- self .old_repo .add_pkgs (pkgs )
539524 try :
540525 old_pkg = self .old_repo .match (atom )[0 ]
541526 new_pkg = pkgs [0 ]
0 commit comments