ยปCore Development>Code coverage>Lib/compileall.py

Python code coverage for Lib/compileall.py

#countcontent
1n/a"""Module/script to byte-compile all .py files to .pyc files.
2n/a
3n/aWhen called as a script with arguments, this compiles the directories
4n/agiven as arguments recursively; the -l option prevents it from
5n/arecursing into directories.
6n/a
7n/aWithout arguments, if compiles all modules on sys.path, without
8n/arecursing into subdirectories. (Even though it should do so for
9n/apackages -- for now, you'll have to deal with packages separately.)
10n/a
11n/aSee module py_compile for details of the actual byte-compilation.
12n/a"""
13n/aimport os
14n/aimport sys
15n/aimport importlib.util
16n/aimport py_compile
17n/aimport struct
18n/a
19n/atry:
20n/a from concurrent.futures import ProcessPoolExecutor
21n/aexcept ImportError:
22n/a ProcessPoolExecutor = None
23n/afrom functools import partial
24n/a
25n/a__all__ = ["compile_dir","compile_file","compile_path"]
26n/a
27n/adef _walk_dir(dir, ddir=None, maxlevels=10, quiet=0):
28n/a if quiet < 2 and isinstance(dir, os.PathLike):
29n/a dir = os.fspath(dir)
30n/a if not quiet:
31n/a print('Listing {!r}...'.format(dir))
32n/a try:
33n/a names = os.listdir(dir)
34n/a except OSError:
35n/a if quiet < 2:
36n/a print("Can't list {!r}".format(dir))
37n/a names = []
38n/a names.sort()
39n/a for name in names:
40n/a if name == '__pycache__':
41n/a continue
42n/a fullname = os.path.join(dir, name)
43n/a if ddir is not None:
44n/a dfile = os.path.join(ddir, name)
45n/a else:
46n/a dfile = None
47n/a if not os.path.isdir(fullname):
48n/a yield fullname
49n/a elif (maxlevels > 0 and name != os.curdir and name != os.pardir and
50n/a os.path.isdir(fullname) and not os.path.islink(fullname)):
51n/a yield from _walk_dir(fullname, ddir=dfile,
52n/a maxlevels=maxlevels - 1, quiet=quiet)
53n/a
54n/adef compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
55n/a quiet=0, legacy=False, optimize=-1, workers=1):
56n/a """Byte-compile all modules in the given directory tree.
57n/a
58n/a Arguments (only dir is required):
59n/a
60n/a dir: the directory to byte-compile
61n/a maxlevels: maximum recursion level (default 10)
62n/a ddir: the directory that will be prepended to the path to the
63n/a file as it is compiled into each byte-code file.
64n/a force: if True, force compilation, even if timestamps are up-to-date
65n/a quiet: full output with False or 0, errors only with 1,
66n/a no output with 2
67n/a legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
68n/a optimize: optimization level or -1 for level of the interpreter
69n/a workers: maximum number of parallel workers
70n/a """
71n/a if workers is not None and workers < 0:
72n/a raise ValueError('workers must be greater or equal to 0')
73n/a
74n/a files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels,
75n/a ddir=ddir)
76n/a success = True
77n/a if workers is not None and workers != 1 and ProcessPoolExecutor is not None:
78n/a workers = workers or None
79n/a with ProcessPoolExecutor(max_workers=workers) as executor:
80n/a results = executor.map(partial(compile_file,
81n/a ddir=ddir, force=force,
82n/a rx=rx, quiet=quiet,
83n/a legacy=legacy,
84n/a optimize=optimize),
85n/a files)
86n/a success = min(results, default=True)
87n/a else:
88n/a for file in files:
89n/a if not compile_file(file, ddir, force, rx, quiet,
90n/a legacy, optimize):
91n/a success = False
92n/a return success
93n/a
94n/adef compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
95n/a legacy=False, optimize=-1):
96n/a """Byte-compile one file.
97n/a
98n/a Arguments (only fullname is required):
99n/a
100n/a fullname: the file to byte-compile
101n/a ddir: if given, the directory name compiled in to the
102n/a byte-code file.
103n/a force: if True, force compilation, even if timestamps are up-to-date
104n/a quiet: full output with False or 0, errors only with 1,
105n/a no output with 2
106n/a legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
107n/a optimize: optimization level or -1 for level of the interpreter
108n/a """
109n/a success = True
110n/a if quiet < 2 and isinstance(fullname, os.PathLike):
111n/a fullname = os.fspath(fullname)
112n/a name = os.path.basename(fullname)
113n/a if ddir is not None:
114n/a dfile = os.path.join(ddir, name)
115n/a else:
116n/a dfile = None
117n/a if rx is not None:
118n/a mo = rx.search(fullname)
119n/a if mo:
120n/a return success
121n/a if os.path.isfile(fullname):
122n/a if legacy:
123n/a cfile = fullname + 'c'
124n/a else:
125n/a if optimize >= 0:
126n/a opt = optimize if optimize >= 1 else ''
127n/a cfile = importlib.util.cache_from_source(
128n/a fullname, optimization=opt)
129n/a else:
130n/a cfile = importlib.util.cache_from_source(fullname)
131n/a cache_dir = os.path.dirname(cfile)
132n/a head, tail = name[:-3], name[-3:]
133n/a if tail == '.py':
134n/a if not force:
135n/a try:
136n/a mtime = int(os.stat(fullname).st_mtime)
137n/a expect = struct.pack('<4sl', importlib.util.MAGIC_NUMBER,
138n/a mtime)
139n/a with open(cfile, 'rb') as chandle:
140n/a actual = chandle.read(8)
141n/a if expect == actual:
142n/a return success
143n/a except OSError:
144n/a pass
145n/a if not quiet:
146n/a print('Compiling {!r}...'.format(fullname))
147n/a try:
148n/a ok = py_compile.compile(fullname, cfile, dfile, True,
149n/a optimize=optimize)
150n/a except py_compile.PyCompileError as err:
151n/a success = False
152n/a if quiet >= 2:
153n/a return success
154n/a elif quiet:
155n/a print('*** Error compiling {!r}...'.format(fullname))
156n/a else:
157n/a print('*** ', end='')
158n/a # escape non-printable characters in msg
159n/a msg = err.msg.encode(sys.stdout.encoding,
160n/a errors='backslashreplace')
161n/a msg = msg.decode(sys.stdout.encoding)
162n/a print(msg)
163n/a except (SyntaxError, UnicodeError, OSError) as e:
164n/a success = False
165n/a if quiet >= 2:
166n/a return success
167n/a elif quiet:
168n/a print('*** Error compiling {!r}...'.format(fullname))
169n/a else:
170n/a print('*** ', end='')
171n/a print(e.__class__.__name__ + ':', e)
172n/a else:
173n/a if ok == 0:
174n/a success = False
175n/a return success
176n/a
177n/adef compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=0,
178n/a legacy=False, optimize=-1):
179n/a """Byte-compile all module on sys.path.
180n/a
181n/a Arguments (all optional):
182n/a
183n/a skip_curdir: if true, skip current directory (default True)
184n/a maxlevels: max recursion level (default 0)
185n/a force: as for compile_dir() (default False)
186n/a quiet: as for compile_dir() (default 0)
187n/a legacy: as for compile_dir() (default False)
188n/a optimize: as for compile_dir() (default -1)
189n/a """
190n/a success = True
191n/a for dir in sys.path:
192n/a if (not dir or dir == os.curdir) and skip_curdir:
193n/a if quiet < 2:
194n/a print('Skipping current directory')
195n/a else:
196n/a success = success and compile_dir(dir, maxlevels, None,
197n/a force, quiet=quiet,
198n/a legacy=legacy, optimize=optimize)
199n/a return success
200n/a
201n/a
202n/adef main():
203n/a """Script main program."""
204n/a import argparse
205n/a
206n/a parser = argparse.ArgumentParser(
207n/a description='Utilities to support installing Python libraries.')
208n/a parser.add_argument('-l', action='store_const', const=0,
209n/a default=10, dest='maxlevels',
210n/a help="don't recurse into subdirectories")
211n/a parser.add_argument('-r', type=int, dest='recursion',
212n/a help=('control the maximum recursion level. '
213n/a 'if `-l` and `-r` options are specified, '
214n/a 'then `-r` takes precedence.'))
215n/a parser.add_argument('-f', action='store_true', dest='force',
216n/a help='force rebuild even if timestamps are up to date')
217n/a parser.add_argument('-q', action='count', dest='quiet', default=0,
218n/a help='output only error messages; -qq will suppress '
219n/a 'the error messages as well.')
220n/a parser.add_argument('-b', action='store_true', dest='legacy',
221n/a help='use legacy (pre-PEP3147) compiled file locations')
222n/a parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None,
223n/a help=('directory to prepend to file paths for use in '
224n/a 'compile-time tracebacks and in runtime '
225n/a 'tracebacks in cases where the source file is '
226n/a 'unavailable'))
227n/a parser.add_argument('-x', metavar='REGEXP', dest='rx', default=None,
228n/a help=('skip files matching the regular expression; '
229n/a 'the regexp is searched for in the full path '
230n/a 'of each file considered for compilation'))
231n/a parser.add_argument('-i', metavar='FILE', dest='flist',
232n/a help=('add all the files and directories listed in '
233n/a 'FILE to the list considered for compilation; '
234n/a 'if "-", names are read from stdin'))
235n/a parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='*',
236n/a help=('zero or more file and directory names '
237n/a 'to compile; if no arguments given, defaults '
238n/a 'to the equivalent of -l sys.path'))
239n/a parser.add_argument('-j', '--workers', default=1,
240n/a type=int, help='Run compileall concurrently')
241n/a
242n/a args = parser.parse_args()
243n/a compile_dests = args.compile_dest
244n/a
245n/a if args.rx:
246n/a import re
247n/a args.rx = re.compile(args.rx)
248n/a
249n/a
250n/a if args.recursion is not None:
251n/a maxlevels = args.recursion
252n/a else:
253n/a maxlevels = args.maxlevels
254n/a
255n/a # if flist is provided then load it
256n/a if args.flist:
257n/a try:
258n/a with (sys.stdin if args.flist=='-' else open(args.flist)) as f:
259n/a for line in f:
260n/a compile_dests.append(line.strip())
261n/a except OSError:
262n/a if args.quiet < 2:
263n/a print("Error reading file list {}".format(args.flist))
264n/a return False
265n/a
266n/a if args.workers is not None:
267n/a args.workers = args.workers or None
268n/a
269n/a success = True
270n/a try:
271n/a if compile_dests:
272n/a for dest in compile_dests:
273n/a if os.path.isfile(dest):
274n/a if not compile_file(dest, args.ddir, args.force, args.rx,
275n/a args.quiet, args.legacy):
276n/a success = False
277n/a else:
278n/a if not compile_dir(dest, maxlevels, args.ddir,
279n/a args.force, args.rx, args.quiet,
280n/a args.legacy, workers=args.workers):
281n/a success = False
282n/a return success
283n/a else:
284n/a return compile_path(legacy=args.legacy, force=args.force,
285n/a quiet=args.quiet)
286n/a except KeyboardInterrupt:
287n/a if args.quiet < 2:
288n/a print("\n[interrupted]")
289n/a return False
290n/a return True
291n/a
292n/a
293n/aif __name__ == '__main__':
294n/a exit_status = int(not main())
295n/a sys.exit(exit_status)