Skip to content

Commit

Permalink
Update min python version from 3.6 to 3.8
Browse files Browse the repository at this point in the history
The reason for picking 3.8 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 (focal): 3.8
- emsdk: 3.9.2

Replaces: #23378
Fixes: #23387
  • Loading branch information
sbc100 committed Jan 15, 2025
1 parent bcad96d commit 77ffc22
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 80 deletions.
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ See docs/process.md for more on how version tagging works.
- The minimum version of node required to run emscripten was bumped from v16.20
to v18. Version 4.0 was mistakenly shipped with a change that required v20,
but that was reverted. (#23410)
- The version of python required to run emscripten was bumped from 3.6 to 3.8.
(#23417)

4.0.0 - 01/14/25
----------------
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ lint.ignore = [
"B006",
"B011",
"B018",
"B019",
"B023",
"B026",
"B904",
Expand Down
13 changes: 1 addition & 12 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,17 +476,6 @@ def check_closure_compiler(cmd, args, env, allowed_to_fail):
return True


# Remove this once we require python3.7 and can use std.isascii.
# See: https://docs.python.org/3/library/stdtypes.html#str.isascii
def isascii(s):
try:
s.encode('ascii')
except UnicodeEncodeError:
return False
else:
return True


def get_closure_compiler_and_env(user_args):
env = shared.env_with_node_in_path()
closure_cmd = get_closure_compiler()
Expand Down Expand Up @@ -623,7 +612,7 @@ def run_closure_cmd(cmd, filename, env):
tempfiles = shared.get_temp_files()

def move_to_safe_7bit_ascii_filename(filename):
if isascii(filename):
if filename.isascii():
return os.path.abspath(filename)
safe_filename = tempfiles.get('.js').name # Safe 7-bit filename
shutil.copyfile(filename, safe_filename)
Expand Down
3 changes: 2 additions & 1 deletion tools/extract_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# found in the LICENSE file.

import logging
from functools import cache
from typing import List, Dict

from . import webassembly, utils
Expand Down Expand Up @@ -140,7 +141,7 @@ def parse_function_for_memory_inits(module, func_index, offset_map):
parse_function_for_memory_inits(module, t, offset_map)


@webassembly.memoize
@cache
def get_passive_segment_offsets(module):
start_func_index = module.get_start()
assert start_func_index is not None
Expand Down
25 changes: 5 additions & 20 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from .toolchain_profiler import ToolchainProfiler

from enum import Enum, unique, auto
from functools import wraps
from subprocess import PIPE
import functools
import atexit
import json
import logging
Expand All @@ -20,9 +20,9 @@
import sys
import tempfile

# We depend on python 3.6 for fstring support
if sys.version_info < (3, 6):
print('error: emscripten requires python 3.6 or above', file=sys.stderr)
# We depend on python 3.8 features
if sys.version_info < (3, 8):
print(f'error: emscripten requires python 3.8 or above ({sys.executable} {sys.version})', file=sys.stderr)
sys.exit(1)

from . import colored_logger
Expand Down Expand Up @@ -67,6 +67,7 @@
EMSCRIPTEN_TEMP_DIR = None

logger = logging.getLogger('shared')
memoize = functools.cache

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


# TODO(sbc): Replace with functools.cache, once we update to python 3.7
def memoize(func):
called = False
result = None

@wraps(func)
def helper():
nonlocal called, result
if not called:
result = func()
called = True
return result

return helper


@memoize
def get_clang_version():
if not os.path.exists(CLANG_CC):
Expand Down
11 changes: 1 addition & 10 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2442,17 +2442,8 @@ def calculate(args):
return ret


# Once we require python 3.8 we can use shutil.copytree with
# dirs_exist_ok=True and remove this function.
def copytree_exist_ok(src, dst):
os.makedirs(dst, exist_ok=True)
for entry in os.scandir(src):
srcname = os.path.join(src, entry.name)
dstname = os.path.join(dst, entry.name)
if entry.is_dir():
copytree_exist_ok(srcname, dstname)
else:
shared.safe_copy(srcname, dstname)
shutil.copytree(src, dst, dirs_exist_ok=True)


def install_system_headers(stamp):
Expand Down
50 changes: 13 additions & 37 deletions tools/webassembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"""Utilities for manipulating WebAssembly binaries from python.
"""

from functools import cache
from collections import namedtuple
from enum import IntEnum
from functools import wraps
import logging
import os
import sys
Expand Down Expand Up @@ -55,30 +55,6 @@ def read_sleb(iobuf):
return leb128.i.decode_reader(iobuf)[0]


def memoize(method):

@wraps(method)
def wrapper(self, *args, **kwargs):
assert not kwargs
key = (method.__name__, args)
if key not in self._cache:
self._cache[key] = method(self, *args, **kwargs)
return self._cache[key]

return wrapper


def once(method):

@wraps(method)
def helper(self, *args, **kwargs):
key = method
if key not in self._cache:
self._cache[key] = method(self, *args, **kwargs)

return helper


class Type(IntEnum):
I32 = 0x7f # -0x1
I64 = 0x7e # -0x2
Expand Down Expand Up @@ -280,7 +256,7 @@ def sections(self):
yield Section(section_type, section_size, section_offset, name)
offset = section_offset + section_size

@memoize
@cache
def get_types(self):
type_section = self.get_section(SecType.TYPE)
if not type_section:
Expand Down Expand Up @@ -315,7 +291,7 @@ def parse_features_section(self):
feature_count -= 1
return features

@memoize
@cache
def parse_dylink_section(self):
dylink_section = next(self.sections())
assert dylink_section.type == SecType.CUSTOM
Expand Down Expand Up @@ -380,7 +356,7 @@ def parse_dylink_section(self):

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

@memoize
@cache
def get_exports(self):
export_section = self.get_section(SecType.EXPORT)
if not export_section:
Expand All @@ -397,7 +373,7 @@ def get_exports(self):

return exports

@memoize
@cache
def get_imports(self):
import_section = self.get_section(SecType.IMPORT)
if not import_section:
Expand Down Expand Up @@ -430,7 +406,7 @@ def get_imports(self):

return imports

@memoize
@cache
def get_globals(self):
global_section = self.get_section(SecType.GLOBAL)
if not global_section:
Expand All @@ -445,15 +421,15 @@ def get_globals(self):
globls.append(Global(global_type, mutable, init))
return globls

@memoize
@cache
def get_start(self):
start_section = self.get_section(SecType.START)
if not start_section:
return None
self.seek(start_section.offset)
return self.read_uleb()

@memoize
@cache
def get_functions(self):
code_section = self.get_section(SecType.CODE)
if not code_section:
Expand All @@ -471,14 +447,14 @@ def get_functions(self):
def get_section(self, section_code):
return next((s for s in self.sections() if s.type == section_code), None)

@memoize
@cache
def get_custom_section(self, name):
for section in self.sections():
if section.type == SecType.CUSTOM and section.name == name:
return section
return None

@memoize
@cache
def get_segments(self):
segments = []
data_section = self.get_section(SecType.DATA)
Expand All @@ -496,7 +472,7 @@ def get_segments(self):
self.seek(offset + size)
return segments

@memoize
@cache
def get_tables(self):
table_section = self.get_section(SecType.TABLE)
if not table_section:
Expand All @@ -512,7 +488,7 @@ def get_tables(self):

return tables

@memoize
@cache
def get_function_types(self):
function_section = self.get_section(SecType.FUNCTION)
if not function_section:
Expand All @@ -525,7 +501,7 @@ def get_function_types(self):
def has_name_section(self):
return self.get_custom_section('name') is not None

@once
@cache
def _calc_indexes(self):
self.imports_by_kind = {}
for i in self.get_imports():
Expand Down

0 comments on commit 77ffc22

Please sign in to comment.