Skip to content

Commit 08bb5f8

Browse files
committed
Update min python version to 3.9
The reason for picking 3.9 here is that it provides all the features we currently have need of, and it available in the places we care about: - debian/stable (bookworm): 3.11 - ubuntu/LTS (jammy): 3.10 - emsdk: 3.9.2 It also seems like a good idea to choose the emsdk version since that is the version we use for testing and we don't currently have any mechanism to test on anything older than that (which means we currently lack any way to confirm that we really do support 3.6). Replaces: emscripten-core#23378 Fixes: emscripten-core#23387
1 parent bcad96d commit 08bb5f8

7 files changed

+25
-80
lines changed

ChangeLog.md

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ See docs/process.md for more on how version tagging works.
2323
- The minimum version of node required to run emscripten was bumped from v16.20
2424
to v18. Version 4.0 was mistakenly shipped with a change that required v20,
2525
but that was reverted. (#23410)
26+
- The version of python required to run emscripten was bumped from python3.6 to
27+
python3.9. (#23417)
2628

2729
4.0.0 - 01/14/25
2830
----------------

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ lint.ignore = [
3333
"B006",
3434
"B011",
3535
"B018",
36+
"B019",
3637
"B023",
3738
"B026",
3839
"B904",

tools/building.py

+1-12
Original file line numberDiff line numberDiff line change
@@ -476,17 +476,6 @@ def check_closure_compiler(cmd, args, env, allowed_to_fail):
476476
return True
477477

478478

479-
# Remove this once we require python3.7 and can use std.isascii.
480-
# See: https://docs.python.org/3/library/stdtypes.html#str.isascii
481-
def isascii(s):
482-
try:
483-
s.encode('ascii')
484-
except UnicodeEncodeError:
485-
return False
486-
else:
487-
return True
488-
489-
490479
def get_closure_compiler_and_env(user_args):
491480
env = shared.env_with_node_in_path()
492481
closure_cmd = get_closure_compiler()
@@ -623,7 +612,7 @@ def run_closure_cmd(cmd, filename, env):
623612
tempfiles = shared.get_temp_files()
624613

625614
def move_to_safe_7bit_ascii_filename(filename):
626-
if isascii(filename):
615+
if filename.isascii():
627616
return os.path.abspath(filename)
628617
safe_filename = tempfiles.get('.js').name # Safe 7-bit filename
629618
shutil.copyfile(filename, safe_filename)

tools/extract_metadata.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# found in the LICENSE file.
55

66
import logging
7+
from functools import cache
78
from typing import List, Dict
89

910
from . import webassembly, utils
@@ -140,7 +141,7 @@ def parse_function_for_memory_inits(module, func_index, offset_map):
140141
parse_function_for_memory_inits(module, t, offset_map)
141142

142143

143-
@webassembly.memoize
144+
@cache
144145
def get_passive_segment_offsets(module):
145146
start_func_index = module.get_start()
146147
assert start_func_index is not None

tools/shared.py

+5-20
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from .toolchain_profiler import ToolchainProfiler
77

88
from enum import Enum, unique, auto
9-
from functools import wraps
109
from subprocess import PIPE
10+
import functools
1111
import atexit
1212
import json
1313
import logging
@@ -20,9 +20,9 @@
2020
import sys
2121
import tempfile
2222

23-
# We depend on python 3.6 for fstring support
24-
if sys.version_info < (3, 6):
25-
print('error: emscripten requires python 3.6 or above', file=sys.stderr)
23+
# We depend on python 3.9 features
24+
if sys.version_info < (3, 9):
25+
print('error: emscripten requires python 3.9 or above', file=sys.stderr)
2626
sys.exit(1)
2727

2828
from . import colored_logger
@@ -67,6 +67,7 @@
6767
EMSCRIPTEN_TEMP_DIR = None
6868

6969
logger = logging.getLogger('shared')
70+
memoize = functools.cache
7071

7172
# warning about absolute-paths is disabled by default, and not enabled by -Wall
7273
diagnostics.add_warning('absolute-paths', enabled=False, part_of_all=False)
@@ -273,22 +274,6 @@ def get_npm_cmd(name):
273274
return cmd
274275

275276

276-
# TODO(sbc): Replace with functools.cache, once we update to python 3.7
277-
def memoize(func):
278-
called = False
279-
result = None
280-
281-
@wraps(func)
282-
def helper():
283-
nonlocal called, result
284-
if not called:
285-
result = func()
286-
called = True
287-
return result
288-
289-
return helper
290-
291-
292277
@memoize
293278
def get_clang_version():
294279
if not os.path.exists(CLANG_CC):

tools/system_libs.py

+1-10
Original file line numberDiff line numberDiff line change
@@ -2442,17 +2442,8 @@ def calculate(args):
24422442
return ret
24432443

24442444

2445-
# Once we require python 3.8 we can use shutil.copytree with
2446-
# dirs_exist_ok=True and remove this function.
24472445
def copytree_exist_ok(src, dst):
2448-
os.makedirs(dst, exist_ok=True)
2449-
for entry in os.scandir(src):
2450-
srcname = os.path.join(src, entry.name)
2451-
dstname = os.path.join(dst, entry.name)
2452-
if entry.is_dir():
2453-
copytree_exist_ok(srcname, dstname)
2454-
else:
2455-
shared.safe_copy(srcname, dstname)
2446+
shutil.copytree(src, dst, dirs_exist_ok=True)
24562447

24572448

24582449
def install_system_headers(stamp):

tools/webassembly.py

+13-37
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"""Utilities for manipulating WebAssembly binaries from python.
77
"""
88

9+
from functools import cache
910
from collections import namedtuple
1011
from enum import IntEnum
11-
from functools import wraps
1212
import logging
1313
import os
1414
import sys
@@ -55,30 +55,6 @@ def read_sleb(iobuf):
5555
return leb128.i.decode_reader(iobuf)[0]
5656

5757

58-
def memoize(method):
59-
60-
@wraps(method)
61-
def wrapper(self, *args, **kwargs):
62-
assert not kwargs
63-
key = (method.__name__, args)
64-
if key not in self._cache:
65-
self._cache[key] = method(self, *args, **kwargs)
66-
return self._cache[key]
67-
68-
return wrapper
69-
70-
71-
def once(method):
72-
73-
@wraps(method)
74-
def helper(self, *args, **kwargs):
75-
key = method
76-
if key not in self._cache:
77-
self._cache[key] = method(self, *args, **kwargs)
78-
79-
return helper
80-
81-
8258
class Type(IntEnum):
8359
I32 = 0x7f # -0x1
8460
I64 = 0x7e # -0x2
@@ -280,7 +256,7 @@ def sections(self):
280256
yield Section(section_type, section_size, section_offset, name)
281257
offset = section_offset + section_size
282258

283-
@memoize
259+
@cache
284260
def get_types(self):
285261
type_section = self.get_section(SecType.TYPE)
286262
if not type_section:
@@ -315,7 +291,7 @@ def parse_features_section(self):
315291
feature_count -= 1
316292
return features
317293

318-
@memoize
294+
@cache
319295
def parse_dylink_section(self):
320296
dylink_section = next(self.sections())
321297
assert dylink_section.type == SecType.CUSTOM
@@ -380,7 +356,7 @@ def parse_dylink_section(self):
380356

381357
return Dylink(mem_size, mem_align, table_size, table_align, needed, export_info, import_info)
382358

383-
@memoize
359+
@cache
384360
def get_exports(self):
385361
export_section = self.get_section(SecType.EXPORT)
386362
if not export_section:
@@ -397,7 +373,7 @@ def get_exports(self):
397373

398374
return exports
399375

400-
@memoize
376+
@cache
401377
def get_imports(self):
402378
import_section = self.get_section(SecType.IMPORT)
403379
if not import_section:
@@ -430,7 +406,7 @@ def get_imports(self):
430406

431407
return imports
432408

433-
@memoize
409+
@cache
434410
def get_globals(self):
435411
global_section = self.get_section(SecType.GLOBAL)
436412
if not global_section:
@@ -445,15 +421,15 @@ def get_globals(self):
445421
globls.append(Global(global_type, mutable, init))
446422
return globls
447423

448-
@memoize
424+
@cache
449425
def get_start(self):
450426
start_section = self.get_section(SecType.START)
451427
if not start_section:
452428
return None
453429
self.seek(start_section.offset)
454430
return self.read_uleb()
455431

456-
@memoize
432+
@cache
457433
def get_functions(self):
458434
code_section = self.get_section(SecType.CODE)
459435
if not code_section:
@@ -471,14 +447,14 @@ def get_functions(self):
471447
def get_section(self, section_code):
472448
return next((s for s in self.sections() if s.type == section_code), None)
473449

474-
@memoize
450+
@cache
475451
def get_custom_section(self, name):
476452
for section in self.sections():
477453
if section.type == SecType.CUSTOM and section.name == name:
478454
return section
479455
return None
480456

481-
@memoize
457+
@cache
482458
def get_segments(self):
483459
segments = []
484460
data_section = self.get_section(SecType.DATA)
@@ -496,7 +472,7 @@ def get_segments(self):
496472
self.seek(offset + size)
497473
return segments
498474

499-
@memoize
475+
@cache
500476
def get_tables(self):
501477
table_section = self.get_section(SecType.TABLE)
502478
if not table_section:
@@ -512,7 +488,7 @@ def get_tables(self):
512488

513489
return tables
514490

515-
@memoize
491+
@cache
516492
def get_function_types(self):
517493
function_section = self.get_section(SecType.FUNCTION)
518494
if not function_section:
@@ -525,7 +501,7 @@ def get_function_types(self):
525501
def has_name_section(self):
526502
return self.get_custom_section('name') is not None
527503

528-
@once
504+
@cache
529505
def _calc_indexes(self):
530506
self.imports_by_kind = {}
531507
for i in self.get_imports():

0 commit comments

Comments
 (0)