summaryrefslogtreecommitdiff
path: root/debian/scripts/pypyclean
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2019-12-02 13:09:17 +0300
committerIgor Pashev <pashev.igor@gmail.com>2019-12-02 13:09:17 +0300
commit38fde63f74091af1f6a0d485474769bb6b4f17ce (patch)
tree1317a1fa2ef61c710ff5c653f43c0af8bb164ca6 /debian/scripts/pypyclean
downloadpypy-debian.tar.gz
Import pypy (7.2.0+dfsg-1)debian/7.2.0+dfsg-1debian
Diffstat (limited to 'debian/scripts/pypyclean')
-rwxr-xr-xdebian/scripts/pypyclean200
1 files changed, 200 insertions, 0 deletions
diff --git a/debian/scripts/pypyclean b/debian/scripts/pypyclean
new file mode 100755
index 0000000..83c8907
--- /dev/null
+++ b/debian/scripts/pypyclean
@@ -0,0 +1,200 @@
+#!/usr/bin/pypy
+
+import argparse
+import collections
+import itertools
+import os
+import shutil
+import subprocess
+import sys
+
+
+def abort(message):
+ print >> sys.stderr, message
+ sys.exit(1)
+
+
+def package_modules(package):
+ '''Iterate through all python modules in an installed Debian package'''
+ p = subprocess.Popen(('dpkg', '-L', package), stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ files, stderr = p.communicate()
+ if p.returncode != 0:
+ abort('Unable to list files in %s. Is it installed?' % package)
+
+ for fn in files.splitlines():
+ if fn.endswith('.py'):
+ if fn.startswith('/usr/share/doc/'):
+ continue
+ yield fn
+
+
+def installed_namespaces():
+ '''Return a dictionary of package: frozenset(namespaces)'''
+ ns_dir = '/usr/share/pypy/ns'
+ ns_by_pkg = {}
+ for pkg in os.listdir(ns_dir):
+ ns_file = os.path.join(ns_dir, pkg)
+ with open(ns_file) as f:
+ contents = f.read().decode('utf-8').strip()
+ namespaces = [line.strip() for line in contents.splitlines()]
+ ns_by_pkg[pkg] = frozenset(namespaces)
+ return ns_by_pkg
+
+
+def cleanup_namespaces(package, verbose):
+ '''Check if a namespace is still being used and, if not:
+ Remove the __init__.py.
+ Remove any pycs related to it.
+ '''
+ ns_by_pkg = installed_namespaces()
+ pkg_namespaces = ns_by_pkg.pop(package, None)
+ if not pkg_namespaces:
+ return
+ foreign_namespaces = reduce(lambda a, b: a | b, ns_by_pkg.values(), set())
+ orphan_namespaces = pkg_namespaces - foreign_namespaces
+ inits_to_remove = []
+ for namespace in orphan_namespaces:
+ init = os.path.join('/usr/lib/pypy/dist-packages',
+ namespace.replace('.', '/'),
+ '__init__.py')
+ if not os.path.exists(init):
+ print 'Missing namespace init: %s' % init
+ continue
+ if os.path.getsize(init) != 0:
+ print 'Non-empty init, ignoring: %s' % init
+ continue
+ inits_to_remove.append(init)
+
+ clean_modules(inits_to_remove, verbose)
+ for init in inits_to_remove:
+ os.unlink(init)
+
+
+def cleanup_package_modules(package, verbose):
+ '''Iterate through all python modules in an installed Debian package that
+ were in /usr/share/doc/, and previously byte-compiled by
+ pypy << 6.0.0+dfsg-2. See #904521
+
+ Can be removed once every pypy library has been re-uploaded, since this was
+ added.
+ '''
+ p = subprocess.Popen(('dpkg', '-L', package), stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ files, stderr = p.communicate()
+ if p.returncode != 0:
+ abort('Unable to list files in %s. Is it installed?' % package)
+
+ modules = [
+ fn for fn in files.splitlines()
+ if fn.startswith('/usr/share/doc/') and fn.endswith('.py')]
+
+ try:
+ clean_modules(modules, verbose)
+ except IOError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+
+def find_modules(root):
+ '''Iterate through all python modules in directory tree root'''
+ if os.path.isfile(root):
+ yield root
+ return
+
+ for dirpath, dirnames, filenames in os.walk(root):
+ for fn in filenames:
+ if fn.endswith('.py'):
+ yield os.path.join(dirpath, fn)
+
+
+def clean_modules(modules, verbose):
+ '''Remove all .pyc files for every module specified'''
+ clean = collections.defaultdict(list)
+ for module in modules:
+ dir_, basename = os.path.split(module)
+ clean[dir_].append(os.path.splitext(basename)[0])
+
+ for dir_, basenames in clean.iteritems():
+ pycache = os.path.join(dir_, '__pycache__')
+ if not os.path.exists(pycache):
+ continue
+
+ empty = True
+ for fn in os.listdir(pycache):
+ if fn.endswith('.pyc') and fn.rsplit('.', 2)[0] in basenames:
+ if verbose:
+ print 'Removing %s' % os.path.join(pycache, fn)
+ os.unlink(os.path.join(pycache, fn))
+ else:
+ empty = False
+
+ if empty:
+ if verbose:
+ print 'Pruning %s' % pycache
+ os.rmdir(pycache)
+
+
+def clean_directories(directories, verbose):
+ '''Indiscriminately remove __pycache__ directories'''
+ for root in directories:
+ for dirpath, dirnames, filenames in os.walk(root):
+ for dir_ in dirnames:
+ if dir_ == '__pycache__':
+ if verbose:
+ print 'Removing %s' % os.path.join(dirpath, dir_)
+ shutil.rmtree(os.path.join(dirpath, dir_))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Remove byte-compiled files for a package')
+ parser.add_argument('-p', '--package', metavar='PACKAGE',
+ action='append', default=[],
+ help='Debian package to byte-compile '
+ '(may be specified multiple times)')
+ parser.add_argument('directory', nargs='*',
+ help='Directory tree (or file) to byte-compile')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Be more verbose')
+ parser.add_argument('-q', '--quiet', action='store_true',
+ help='Be quiet')
+ args = parser.parse_args()
+
+ if not (args.package or args.directory):
+ parser.error('Either a package or a directory must be specified')
+ if args.quiet and args.verbose:
+ parser.error('--quiet and --verbose cannot both be specified')
+
+ modules_p = set(itertools.chain(*(
+ package_modules(package) for package in args.package)))
+ modules_d = set(itertools.chain(*(
+ find_modules(dir_) for dir_ in args.directory)))
+
+ if args.package and args.directory:
+ modules = modules_d & modules_p
+ elif args.package:
+ modules = modules_p
+ else:
+ # Split files from directories, so that we can completely clean any
+ # specified directories.
+ modules = set()
+ directories = set()
+ for fn in args.directory:
+ if os.path.isfile(fn) and fn.endswith('.py'):
+ modules.add(fn)
+ else:
+ directories.add(fn)
+ clean_directories(directories, args.verbose)
+
+ clean_modules(modules, args.verbose)
+
+ for package in args.package:
+ cleanup_namespaces(package, args.verbose)
+ cleanup_package_modules(package, args.verbose)
+
+
+if __name__ == '__main__':
+ main()
+
+# vim: ft=python