pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/13d9205f4057eeeef80a25d410ad123876dc60cd

97560d244c08.css" /> bpo-45629: Add a test for the "freeze" tool. (gh-29222) · python/cpython@13d9205 · GitHub
Skip to content

Commit 13d9205

Browse files
bpo-45629: Add a test for the "freeze" tool. (gh-29222)
The "freeze" tool has been part of the repo for a long time. However, it hasn't had any tests in the test suite to guard against regressions. We add such a test here. This is especially important as there has been a lot of change recently related to frozen modules, with more to come. Note that as part of the test we build Python out-of-tree and install it in a temp dir. https://bugs.python.org/issue45629
1 parent 7f61d9d commit 13d9205

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ Tools/unicode/data/
120120
Tools/msi/obj
121121
Tools/ssl/amd64
122122
Tools/ssl/win32
123+
Tools/freeze/test/outdir
123124

124125
# The frozen modules are always generated by the build so we don't
125126
# keep them in the repo. Also see Tools/scripts/freeze_modules.py.

Lib/test/support/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,17 @@ def wrapper(*args, **kw):
372372
return decorator
373373

374374

375+
def skip_if_buildbot(reason=None):
376+
"""Decorator raising SkipTest if running on a buildbot."""
377+
if not reason:
378+
reason = 'not suitable for buildbots'
379+
if sys.platform == 'win32':
380+
isbuildbot = os.environ.get('USERNAME') == 'Buildbot'
381+
else:
382+
isbuildbot = os.environ.get('USER') == 'buildbot'
383+
return unittest.skipIf(isbuildbot, reason)
384+
385+
375386
def system_must_validate_cert(f):
376387
"""Skip the test on TLS certificate validation failures."""
377388
@functools.wraps(f)

Lib/test/test_tools/test_freeze.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Sanity-check tests for the "freeze" tool."""
2+
3+
import sys
4+
import textwrap
5+
import unittest
6+
7+
from test import support
8+
9+
from . import imports_under_tool, skip_if_missing
10+
skip_if_missing('freeze')
11+
with imports_under_tool('freeze', 'test'):
12+
import freeze as helper
13+
14+
15+
@unittest.skipIf(sys.platform.startswith('win'), 'not supported on Windows')
16+
@support.skip_if_buildbot('not all buildbots have enough space')
17+
class TestFreeze(unittest.TestCase):
18+
19+
def test_freeze_simple_script(self):
20+
script = textwrap.dedent("""
21+
import sys
22+
print('running...')
23+
sys.exit(0)
24+
""")
25+
outdir, scriptfile, python = helper.prepare(script)
26+
27+
executable = helper.freeze(python, scriptfile, outdir)
28+
text = helper.run(executable)
29+
self.assertEqual(text, 'running...')

Tools/freeze/test/freeze.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import os
2+
import os.path
3+
import re
4+
import shlex
5+
import shutil
6+
import subprocess
7+
8+
9+
TESTS_DIR = os.path.dirname(__file__)
10+
TOOL_ROOT = os.path.dirname(TESTS_DIR)
11+
SRCDIR = os.path.dirname(os.path.dirname(TOOL_ROOT))
12+
13+
MAKE = shutil.which('make')
14+
GIT = shutil.which('git')
15+
FREEZE = os.path.join(TOOL_ROOT, 'freeze.py')
16+
OUTDIR = os.path.join(TESTS_DIR, 'outdir')
17+
18+
19+
class UnsupportedError(Exception):
20+
"""The operation isn't supported."""
21+
22+
23+
def _run_quiet(cmd, cwd=None):
24+
#print(f'# {" ".join(shlex.quote(a) for a in cmd)}')
25+
return subprocess.run(
26+
cmd,
27+
cwd=cwd,
28+
capture_output=True,
29+
text=True,
30+
check=True,
31+
)
32+
33+
34+
def _run_stdout(cmd, cwd=None):
35+
proc = _run_quiet(cmd, cwd)
36+
return proc.stdout.strip()
37+
38+
39+
def find_opt(args, name):
40+
opt = f'--{name}'
41+
optstart = f'{opt}='
42+
for i, arg in enumerate(args):
43+
if arg == opt or arg.startswith(optstart):
44+
return i
45+
return -1
46+
47+
48+
def ensure_opt(args, name, value):
49+
opt = f'--{name}'
50+
pos = find_opt(args, name)
51+
if value is None:
52+
if pos < 0:
53+
args.append(opt)
54+
else:
55+
args[pos] = opt
56+
elif pos < 0:
57+
args.extend([opt, value])
58+
else:
59+
arg = args[pos]
60+
if arg == opt:
61+
if pos == len(args) - 1:
62+
raise NotImplementedError((args, opt))
63+
args[pos + 1] = value
64+
else:
65+
args[pos] = f'{opt}={value}'
66+
67+
68+
def git_copy_repo(newroot, oldroot):
69+
if not GIT:
70+
raise UnsupportedError('git')
71+
72+
if os.path.exists(newroot):
73+
print(f'updating copied repo {newroot}...')
74+
if newroot == SRCDIR:
75+
raise Exception('this probably isn\'t what you wanted')
76+
_run_quiet([GIT, 'clean', '-d', '-f'], newroot)
77+
_run_quiet([GIT, 'reset'], newroot)
78+
_run_quiet([GIT, 'checkout', '.'], newroot)
79+
_run_quiet([GIT, 'pull', '-f', oldroot], newroot)
80+
else:
81+
print(f'copying repo into {newroot}...')
82+
_run_quiet([GIT, 'clone', oldroot, newroot])
83+
84+
# Copy over any uncommited files.
85+
text = _run_stdout([GIT, 'status', '-s'], oldroot)
86+
for line in text.splitlines():
87+
_, _, relfile = line.strip().partition(' ')
88+
relfile = relfile.strip()
89+
isdir = relfile.endswith(os.path.sep)
90+
relfile = relfile.rstrip(os.path.sep)
91+
srcfile = os.path.join(oldroot, relfile)
92+
dstfile = os.path.join(newroot, relfile)
93+
os.makedirs(os.path.dirname(dstfile), exist_ok=True)
94+
if isdir:
95+
shutil.copytree(srcfile, dstfile, dirs_exist_ok=True)
96+
else:
97+
shutil.copy2(srcfile, dstfile)
98+
99+
100+
def get_makefile_var(builddir, name):
101+
regex = re.compile(rf'^{name} *=\s*(.*?)\s*$')
102+
filename = os.path.join(builddir, 'Makefile')
103+
try:
104+
infile = open(filename)
105+
except FileNotFoundError:
106+
return None
107+
with infile:
108+
for line in infile:
109+
m = regex.match(line)
110+
if m:
111+
value, = m.groups()
112+
return value or ''
113+
return None
114+
115+
116+
def get_config_var(builddir, name):
117+
python = os.path.join(builddir, 'python')
118+
if os.path.isfile(python):
119+
cmd = [python, '-c',
120+
f'import sysconfig; print(sysconfig.get_config_var("{name}"))']
121+
try:
122+
return _run_stdout(cmd)
123+
except subprocess.CalledProcessError:
124+
pass
125+
return get_makefile_var(builddir, name)
126+
127+
128+
##################################
129+
# freezing
130+
131+
def prepare(script=None, outdir=None):
132+
if not outdir:
133+
outdir = OUTDIR
134+
os.makedirs(outdir, exist_ok=True)
135+
136+
# Write the script to disk.
137+
if script:
138+
scriptfile = os.path.join(outdir, 'app.py')
139+
with open(scriptfile, 'w') as outfile:
140+
outfile.write(script)
141+
142+
# Make a copy of the repo to avoid affecting the current build.
143+
srcdir = os.path.join(outdir, 'cpython')
144+
git_copy_repo(srcdir, SRCDIR)
145+
146+
# We use an out-of-tree build (instead of srcdir).
147+
builddir = os.path.join(outdir, 'python-build')
148+
os.makedirs(builddir, exist_ok=True)
149+
150+
# Run configure.
151+
print(f'configuring python in {builddir}...')
152+
cmd = [
153+
os.path.join(srcdir, 'configure'),
154+
*shlex.split(get_config_var(builddir, 'CONFIG_ARGS') or ''),
155+
]
156+
ensure_opt(cmd, 'cache-file', os.path.join(outdir, 'python-config.cache'))
157+
prefix = os.path.join(outdir, 'python-installation')
158+
ensure_opt(cmd, 'prefix', prefix)
159+
_run_quiet(cmd, builddir)
160+
161+
if not MAKE:
162+
raise UnsupportedError('make')
163+
164+
# Build python.
165+
print('building python...')
166+
if os.path.exists(os.path.join(srcdir, 'Makefile')):
167+
# Out-of-tree builds require a clean srcdir.
168+
_run_quiet([MAKE, '-C', srcdir, 'clean'])
169+
_run_quiet([MAKE, '-C', builddir, '-j8'])
170+
171+
# Install the build.
172+
print(f'installing python into {prefix}...')
173+
_run_quiet([MAKE, '-C', builddir, '-j8', 'install'])
174+
python = os.path.join(prefix, 'bin', 'python3')
175+
176+
return outdir, scriptfile, python
177+
178+
179+
def freeze(python, scriptfile, outdir):
180+
if not MAKE:
181+
raise UnsupportedError('make')
182+
183+
print(f'freezing {scriptfile}...')
184+
os.makedirs(outdir, exist_ok=True)
185+
_run_quiet([python, FREEZE, '-o', outdir, scriptfile], outdir)
186+
_run_quiet([MAKE, '-C', os.path.dirname(scriptfile)])
187+
188+
name = os.path.basename(scriptfile).rpartition('.')[0]
189+
executable = os.path.join(outdir, name)
190+
return executable
191+
192+
193+
def run(executable):
194+
return _run_stdout([executable])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy