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