Skip to content

Commit e5c6010

Browse files
committed
main: implement bytecode generation
Signed-off-by: Filipe Laíns <[email protected]>
1 parent ea0d215 commit e5c6010

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

src/installer/__main__.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Installer CLI."""
22

33
import argparse
4+
import compileall
45
import distutils.dist
56
import os
67
import os.path
8+
import py_compile
79
import sys
810
import sysconfig
911

@@ -14,9 +16,10 @@
1416
from installer._compat.typing import TYPE_CHECKING
1517

1618
if TYPE_CHECKING:
17-
from typing import Dict, Optional, Sequence
19+
from typing import Any, Dict, List, Optional, Sequence, Union
1820

1921
from installer._compat.typing import Text
22+
from installer.records import RecordEntry
2023

2124

2225
def main_parser(): # type: () -> argparse.ArgumentParser
@@ -31,6 +34,23 @@ def main_parser(): # type: () -> argparse.ArgumentParser
3134
default="/",
3235
help="destination directory",
3336
)
37+
if sys.version_info >= (3,):
38+
parser.add_argument(
39+
"--optimize",
40+
"-o",
41+
nargs="*",
42+
metavar="level",
43+
type=int,
44+
default=[0, 1],
45+
help="optimization level(s) (default=0, 1)",
46+
)
47+
else:
48+
parser.add_argument(
49+
"--optimize",
50+
"-o",
51+
action="store_true",
52+
help="enable optimization",
53+
)
3454
return parser
3555

3656

@@ -53,6 +73,35 @@ def get_scheme_dict(distribution_name): # type: (Text) -> Dict[str, str]
5373
return scheme_dict
5474

5575

76+
def _compile_records(record_dict, scheme_dict, compile_args):
77+
# type: (Dict[str, List[RecordEntry]], Dict[str, str], Dict[str, Any]) -> None
78+
for scheme, records in record_dict.items():
79+
if scheme not in ("purelib", "platlib"):
80+
continue
81+
for record in records:
82+
target_path = os.path.join(scheme_dict[scheme], record.path)
83+
compileall.compile_file(target_path, **compile_args)
84+
85+
86+
def generate_bytecode(record_dict, scheme_dict, levels, stripdir=None):
87+
# type: (Dict[str, List[RecordEntry]], Dict[str, str], Union[List[int], bool], Optional[str]) -> None
88+
"""Generate bytecode for Python files."""
89+
if sys.version_info[0] == 2:
90+
return _compile_records(record_dict, scheme_dict, {})
91+
92+
assert isinstance(levels, list)
93+
94+
compile_args = {} # type: Dict[str, Any]
95+
if stripdir:
96+
compile_args["stripdir"] = stripdir
97+
if sys.version_info >= (3, 7):
98+
compile_args["invalidation_mode"] = py_compile.PycInvalidationMode.CHECKED_HASH
99+
100+
for level in levels:
101+
compile_args["optimize"] = level
102+
_compile_records(record_dict, scheme_dict, compile_args)
103+
104+
56105
def main(cli_args, program=None):
57106
# type: (Sequence[str], Optional[str]) -> None
58107
"""Process arguments and perform the install."""
@@ -61,6 +110,8 @@ def main(cli_args, program=None):
61110
parser.prog = program
62111
args = parser.parse_args(cli_args)
63112

113+
bytecode_stripdir = None # type: Optional[str]
114+
64115
with installer.sources.WheelFile.open(args.wheel) as source:
65116
scheme_dict = get_scheme_dict(source.distribution)
66117

@@ -76,13 +127,17 @@ def main(cli_args, program=None):
76127
name: os.path.join(args.destdir, os.path.relpath(value, root))
77128
for name, value in scheme_dict.items()
78129
}
130+
bytecode_stripdir = root
79131

80132
destination = installer.destinations.SchemeDictionaryDestination(
81133
scheme_dict,
82134
sys.executable,
83135
installer.utils.get_launcher_kind(),
84136
)
85-
installer.install(source, destination, {})
137+
records = installer.install(source, destination, {})
138+
139+
if args.optimize:
140+
generate_bytecode(records, scheme_dict, args.optimize, bytecode_stripdir)
86141

87142

88143
def entrypoint(): # type: () -> None

0 commit comments

Comments
 (0)