1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
#!/usr/bin/pypy3
import argparse
import itertools
import os
import re
import py_compile
import subprocess
import sys
def abort(message):
print(message, file=sys.stderr)
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.decode('utf8').splitlines():
if fn.endswith('.py'):
if fn.startswith('/usr/share/doc/'):
continue
yield fn
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)
# BCEP support, from py3compile
def get_exclude_patterns_from_dir(name='/usr/share/python3/bcep/'):
"""Return patterns for files that shouldn't be bytecompiled."""
if not os.path.isdir(name):
return
for fn in os.listdir(name):
with open(os.path.join(name, fn), 'r', encoding='utf-8') as lines:
for line in lines:
type_, vrange, dname, pattern = line.split('|', 3)
if not within_vrange(vrange):
continue
pattern = pattern.rstrip('\n')
if type_ == 're':
pattern = re.compile(pattern)
yield type_, dname, pattern
def within_vrange(vrange):
"""Is our interpreter's cpython compatibility level within vrange?"""
min_, sep, max_ = vrange.partition('-')
if min_:
min_ = tuple(map(int, min_.split('.')))
if max_:
max_ = tuple(map(int, max_.split('.')))
version = sys.version_info[:2]
if not min_ and not max_:
return True
if not sep:
return version == min_
if max_ and version >= max_:
return False
if min_ and min_ > version:
return False
return True
def main():
parser = argparse.ArgumentParser(
description='Byte-compile Python source files in a package, for PyPy')
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('-X', '--exclude', metavar='REGEXPR',
action='append', default=[], type=re.compile,
help='Exclude items that match given REGEXPR '
'(may be specified multiple times)')
parser.add_argument('-v', '--verbose', action='store_true',
help='Be more verbose')
parser.add_argument('-q', '--quiet', action='store_true',
help='Be quiet')
parser.add_argument('-V', metavar='VRANGE', dest='vrange',
help=argparse.SUPPRESS)
parser.add_argument('-O', action='store_true', dest='pyo',
help=argparse.SUPPRESS)
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')
if args.vrange:
print('-V is ignored in pypycompile', file=sys.stderr)
if args.pyo:
print('-O is ignored in pypycompile', file=sys.stderr)
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
else:
modules = modules_d | modules_p
modules = filter(lambda module: not any(pattern.match(module)
for pattern in args.exclude),
modules)
bcep_excludes = list(get_exclude_patterns_from_dir())
for module in modules:
skip = False
for pattern in args.exclude:
if pattern.match(module):
skip = True
for type_, dname, pattern in bcep_excludes:
if not module.startswith(dname):
continue
if type_ == 'dir':
skip = True
if type_ == 're' and pattern.match(module):
skip = True
if skip:
continue
if args.verbose:
print('Byte-compiling %s' % module)
try:
py_compile.compile(module, doraise=True)
except py_compile.PyCompileError as e:
if not args.quiet:
print('Failed to byte-compile %s: %s' % (module, e.msg),
file=sys.stderr)
if __name__ == '__main__':
main()
# vim: ft=python
|