Skip to content

Commit 11e99b0

Browse files
committed
#10453: compileall now uses argparse instead of getopt, so -h works.
Patch by Michele Orrù.
1 parent b92dffc commit 11e99b0

File tree

3 files changed

+111
-78
lines changed

3 files changed

+111
-78
lines changed

Lib/compileall.py

Lines changed: 55 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -153,90 +153,68 @@ def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
153153
legacy=legacy)
154154
return success
155155

156-
def expand_args(args, flist):
157-
"""read names in flist and append to args"""
158-
expanded = args[:]
159-
if flist:
160-
try:
161-
if flist == '-':
162-
fd = sys.stdin
163-
else:
164-
fd = open(flist)
165-
while 1:
166-
line = fd.readline()
167-
if not line:
168-
break
169-
expanded.append(line[:-1])
170-
except IOError:
171-
print("Error reading file list %s" % flist)
172-
raise
173-
return expanded
174156

175157
def main():
176158
"""Script main program."""
177-
import getopt
178-
try:
179-
opts, args = getopt.getopt(sys.argv[1:], 'lfqd:x:i:b')
180-
except getopt.error as msg:
181-
print(msg)
182-
print("usage: python compileall.py [-l] [-f] [-q] [-d destdir] "
183-
"[-x regexp] [-i list] [directory|file ...]")
184-
print("-l: don't recurse down")
185-
print("-f: force rebuild even if timestamps are up-to-date")
186-
print("-q: quiet operation")
187-
print("-d destdir: purported directory name for error messages")
188-
print(" if no directory arguments, -l sys.path is assumed")
189-
print("-x regexp: skip files matching the regular expression regexp")
190-
print(" the regexp is searched for in the full path of the file")
191-
print("-i list: expand list with its content "
192-
"(file and directory names)")
193-
print("-b: Produce legacy byte-compile file paths")
194-
sys.exit(2)
195-
maxlevels = 10
196-
ddir = None
197-
force = False
198-
quiet = False
199-
rx = None
200-
flist = None
201-
legacy = False
202-
for o, a in opts:
203-
if o == '-l': maxlevels = 0
204-
if o == '-d': ddir = a
205-
if o == '-f': force = True
206-
if o == '-q': quiet = True
207-
if o == '-x':
208-
import re
209-
rx = re.compile(a)
210-
if o == '-i': flist = a
211-
if o == '-b': legacy = True
212-
if ddir:
213-
if len(args) != 1 and not os.path.isdir(args[0]):
214-
print("-d destdir require exactly one directory argument")
215-
sys.exit(2)
216-
success = 1
159+
import argparse
160+
161+
parser = argparse.ArgumentParser(
162+
description='Utilities to support installing Python libraries.')
163+
parser.add_argument('-l', action='store_const', default=10, const=0,
164+
dest='maxlevels', help="don't recurse down")
165+
parser.add_argument('-f', action='store_true', dest='force',
166+
help='force rebuild even if timestamps are up to date')
167+
parser.add_argument('-q', action='store_true', dest='quiet',
168+
help='quiet operation')
169+
parser.add_argument('-b', action='store_true', dest='legacy',
170+
help='procude legacy byte-compiled file paths')
171+
parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None,
172+
help=('purported directory name for error messages; '
173+
'if no directory arguments, -l sys.path '
174+
'is assumed.'))
175+
parser.add_argument('-x', metavar='REGEXP', dest='rx', default=None,
176+
help=('skip files matching the regular expression.\n\t'
177+
'The regexp is searched for in the full path'
178+
'of the file'))
179+
parser.add_argument('-i', metavar='FILE', dest='flist',
180+
help='expand the list with the contenent of FILE.')
181+
parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='?')
182+
args = parser.parse_args()
183+
184+
if (args.ddir and args.compile_dest != 1 and
185+
not os.path.isdir(args.compile_dest)):
186+
raise argparse.ArgumentError("-d destdir require exactly one "
187+
"directory argument")
188+
if args.rx:
189+
import re
190+
args.rx = re.compile(args.rx)
191+
192+
# if flist is provided then load it
193+
compile_dests = [args.compile_dest]
194+
if args.flist:
195+
with open(args.flist) as f:
196+
files = f.read().split()
197+
compile_dests.extend(files)
198+
217199
try:
218-
if args or flist:
219-
try:
220-
if flist:
221-
args = expand_args(args, flist)
222-
except IOError:
223-
success = 0
224-
if success:
225-
for arg in args:
226-
if os.path.isdir(arg):
227-
if not compile_dir(arg, maxlevels, ddir,
228-
force, rx, quiet, legacy):
229-
success = 0
230-
else:
231-
if not compile_file(arg, ddir, force, rx,
232-
quiet, legacy):
233-
success = 0
200+
if compile_dests:
201+
for dest in compile_dests:
202+
if os.path.isdir(dest):
203+
if not compile_dir(dest, args.maxlevels, args.ddir,
204+
args.force, args.rx, args.quiet,
205+
args.legacy):
206+
return 0
207+
else:
208+
if not compile_file(dest, args.ddir, args.force, args.rx,
209+
args.quiet, args.legacy):
210+
return 0
234211
else:
235-
success = compile_path(legacy=legacy)
212+
return compile_path(legacy=args.legacy)
236213
except KeyboardInterrupt:
237214
print("\n[interrupt]")
238-
success = 0
239-
return success
215+
return 0
216+
return 1
217+
240218

241219
if __name__ == '__main__':
242220
exit_status = int(not main())

Lib/test/test_compileall.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import struct
88
import subprocess
99
import tempfile
10+
import time
1011
import unittest
1112
import io
1213

@@ -112,7 +113,7 @@ def test_error(self):
112113

113114

114115
class CommandLineTests(unittest.TestCase):
115-
"""Test some aspects of compileall's CLI."""
116+
"""Test compileall's CLI."""
116117

117118
def setUp(self):
118119
self.addCleanup(self._cleanup)
@@ -184,6 +185,57 @@ def test_multiple_runs(self):
184185
self.assertTrue(os.path.exists(cachedir))
185186
self.assertFalse(os.path.exists(cachecachedir))
186187

188+
def test_force(self):
189+
retcode = subprocess.call(
190+
(sys.executable, '-m', 'compileall', '-q', self.pkgdir))
191+
self.assertEqual(retcode, 0)
192+
pycpath = imp.cache_from_source(os.path.join(self.pkgdir, 'bar.py'))
193+
# set atime/mtime backward to avoid file timestamp resolution issues
194+
os.utime(pycpath, (time.time()-60,)*2)
195+
access = os.stat(pycpath).st_mtime
196+
retcode = subprocess.call(
197+
(sys.executable, '-m', 'compileall', '-q', '-f', self.pkgdir))
198+
self.assertEqual(retcode, 0)
199+
access2 = os.stat(pycpath).st_mtime
200+
self.assertNotEqual(access, access2)
201+
202+
def test_legacy(self):
203+
# create a new module
204+
newpackage = os.path.join(self.pkgdir, 'spam')
205+
os.mkdir(newpackage)
206+
with open(os.path.join(newpackage, '__init__.py'), 'w'):
207+
pass
208+
with open(os.path.join(newpackage, 'ham.py'), 'w'):
209+
pass
210+
sourcefile = os.path.join(newpackage, 'ham.py')
211+
212+
retcode = subprocess.call(
213+
(sys.executable, '-m', 'compileall', '-q', '-l', self.pkgdir))
214+
self.assertEqual(retcode, 0)
215+
self.assertFalse(os.path.exists(imp.cache_from_source(sourcefile)))
216+
217+
retcode = subprocess.call(
218+
(sys.executable, '-m', 'compileall', '-q', self.pkgdir))
219+
self.assertEqual(retcode, 0)
220+
self.assertTrue(os.path.exists(imp.cache_from_source(sourcefile)))
221+
222+
def test_quiet(self):
223+
noise = subprocess.getoutput('{} -m compileall {}'.format(
224+
sys.executable, self.pkgdir))
225+
quiet = subprocess.getoutput(('{} -m compileall {}'.format(
226+
sys.executable, self.pkgdir)))
227+
self.assertTrue(len(noise) > len(quiet))
228+
229+
def test_regexp(self):
230+
retcode = subprocess.call(
231+
(sys.executable, '-m', 'compileall', '-q', '-x', 'bar.*', self.pkgdir))
232+
self.assertEqual(retcode, 0)
233+
234+
sourcefile = os.path.join(self.pkgdir, 'bar.py')
235+
self.assertFalse(os.path.exists(imp.cache_from_source(sourcefile)))
236+
sourcefile = os.path.join(self.pkgdir, '__init__.py')
237+
self.assertTrue(os.path.exists(imp.cache_from_source(sourcefile)))
238+
187239

188240
def test_main():
189241
support.run_unittest(

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Core and Builtins
3030
Library
3131
-------
3232

33+
- Issue #10453: compileall now uses argparse instead of getopt, and thus
34+
provides clean output when called with '-h'.
35+
3336
- Issue #8078: Add constants for higher baud rates in the termios module.
3437
Patch by Rodolpho Eckhardt.
3538

0 commit comments

Comments
 (0)