1from distutils.util import convert_path 2from distutils import log 3from distutils.errors import DistutilsOptionError 4import os 5import shutil 6 7from setuptools import Command 8 9 10class rotate(Command): 11 """Delete older distributions""" 12 13 description = "delete older distributions, keeping N newest files" 14 user_options = [ 15 ('match=', 'm', "patterns to match (required)"), 16 ('dist-dir=', 'd', "directory where the distributions are"), 17 ('keep=', 'k', "number of matching distributions to keep"), 18 ] 19 20 boolean_options = [] 21 22 def initialize_options(self): 23 self.match = None 24 self.dist_dir = None 25 self.keep = None 26 27 def finalize_options(self): 28 if self.match is None: 29 raise DistutilsOptionError( 30 "Must specify one or more (comma-separated) match patterns " 31 "(e.g. '.zip' or '.egg')" 32 ) 33 if self.keep is None: 34 raise DistutilsOptionError("Must specify number of files to keep") 35 try: 36 self.keep = int(self.keep) 37 except ValueError as e: 38 raise DistutilsOptionError("--keep must be an integer") from e 39 if isinstance(self.match, str): 40 self.match = [ 41 convert_path(p.strip()) for p in self.match.split(',') 42 ] 43 self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) 44 45 def run(self): 46 self.run_command("egg_info") 47 from glob import glob 48 49 for pattern in self.match: 50 pattern = self.distribution.get_name() + '*' + pattern 51 files = glob(os.path.join(self.dist_dir, pattern)) 52 files = [(os.path.getmtime(f), f) for f in files] 53 files.sort() 54 files.reverse() 55 56 log.info("%d file(s) matching %s", len(files), pattern) 57 files = files[self.keep:] 58 for (t, f) in files: 59 log.info("Deleting %s", f) 60 if not self.dry_run: 61 if os.path.isdir(f): 62 shutil.rmtree(f) 63 else: 64 os.unlink(f) 65