Skip to content

Commit 45be345

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 45be345

File tree

5 files changed

+22
-78
lines changed

5 files changed

+22
-78
lines changed

tools/building.py

Lines changed: 1 addition & 12 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from enum import Enum, unique, auto
99
from functools import wraps
1010
from subprocess import PIPE
11+
import functools
1112
import atexit
1213
import json
1314
import logging
@@ -20,9 +21,9 @@
2021
import sys
2122
import tempfile
2223

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)
24+
# We depend on python 3.9 features
25+
if sys.version_info < (3, 9):
26+
print('error: emscripten requires python 3.9 or above', file=sys.stderr)
2627
sys.exit(1)
2728

2829
from . import colored_logger
@@ -67,6 +68,7 @@
6768
EMSCRIPTEN_TEMP_DIR = None
6869

6970
logger = logging.getLogger('shared')
71+
memoize = functools.cache
7072

7173
# warning about absolute-paths is disabled by default, and not enabled by -Wall
7274
diagnostics.add_warning('absolute-paths', enabled=False, part_of_all=False)
@@ -273,22 +275,6 @@ def get_npm_cmd(name):
273275
return cmd
274276

275277

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-
292278
@memoize
293279
def get_clang_version():
294280
if not os.path.exists(CLANG_CC):

tools/system_libs.py

Lines changed: 1 addition & 10 deletions
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

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
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
1112
from functools import wraps
@@ -55,30 +56,6 @@ def read_sleb(iobuf):
5556
return leb128.i.decode_reader(iobuf)[0]
5657

5758

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-
8259
class Type(IntEnum):
8360
I32 = 0x7f # -0x1
8461
I64 = 0x7e # -0x2
@@ -280,7 +257,7 @@ def sections(self):
280257
yield Section(section_type, section_size, section_offset, name)
281258
offset = section_offset + section_size
282259

283-
@memoize
260+
@cache
284261
def get_types(self):
285262
type_section = self.get_section(SecType.TYPE)
286263
if not type_section:
@@ -315,7 +292,7 @@ def parse_features_section(self):
315292
feature_count -= 1
316293
return features
317294

318-
@memoize
295+
@cache
319296
def parse_dylink_section(self):
320297
dylink_section = next(self.sections())
321298
assert dylink_section.type == SecType.CUSTOM
@@ -380,7 +357,7 @@ def parse_dylink_section(self):
380357

381358
return Dylink(mem_size, mem_align, table_size, table_align, needed, export_info, import_info)
382359

383-
@memoize
360+
@cache
384361
def get_exports(self):
385362
export_section = self.get_section(SecType.EXPORT)
386363
if not export_section:
@@ -397,7 +374,7 @@ def get_exports(self):
397374

398375
return exports
399376

400-
@memoize
377+
@cache
401378
def get_imports(self):
402379
import_section = self.get_section(SecType.IMPORT)
403380
if not import_section:
@@ -430,7 +407,7 @@ def get_imports(self):
430407

431408
return imports
432409

433-
@memoize
410+
@cache
434411
def get_globals(self):
435412
global_section = self.get_section(SecType.GLOBAL)
436413
if not global_section:
@@ -445,15 +422,15 @@ def get_globals(self):
445422
globls.append(Global(global_type, mutable, init))
446423
return globls
447424

448-
@memoize
425+
@cache
449426
def get_start(self):
450427
start_section = self.get_section(SecType.START)
451428
if not start_section:
452429
return None
453430
self.seek(start_section.offset)
454431
return self.read_uleb()
455432

456-
@memoize
433+
@cache
457434
def get_functions(self):
458435
code_section = self.get_section(SecType.CODE)
459436
if not code_section:
@@ -471,14 +448,14 @@ def get_functions(self):
471448
def get_section(self, section_code):
472449
return next((s for s in self.sections() if s.type == section_code), None)
473450

474-
@memoize
451+
@cache
475452
def get_custom_section(self, name):
476453
for section in self.sections():
477454
if section.type == SecType.CUSTOM and section.name == name:
478455
return section
479456
return None
480457

481-
@memoize
458+
@cache
482459
def get_segments(self):
483460
segments = []
484461
data_section = self.get_section(SecType.DATA)
@@ -496,7 +473,7 @@ def get_segments(self):
496473
self.seek(offset + size)
497474
return segments
498475

499-
@memoize
476+
@cache
500477
def get_tables(self):
501478
table_section = self.get_section(SecType.TABLE)
502479
if not table_section:
@@ -512,7 +489,7 @@ def get_tables(self):
512489

513490
return tables
514491

515-
@memoize
492+
@cache
516493
def get_function_types(self):
517494
function_section = self.get_section(SecType.FUNCTION)
518495
if not function_section:
@@ -525,7 +502,7 @@ def get_function_types(self):
525502
def has_name_section(self):
526503
return self.get_custom_section('name') is not None
527504

528-
@once
505+
@cache
529506
def _calc_indexes(self):
530507
self.imports_by_kind = {}
531508
for i in self.get_imports():

0 commit comments

Comments
 (0)