Skip to content

Commit 4fabef2

Browse files
authored
Address code duplication in mypyc/lib-rt (#20368)
There is a bunch of code duplication in `mypyc/lib-rt`, but it looks like there is no way to resolve this in a reasonable way. So instead I factor out duplicated code into a separate module, and add a test to verify files are synced. Note I type-check `lib-rt` separately now. Whatever I tried, I am getting weird error either like ``` Cannot find implementation or library stub for module named "build_setup" ``` or ``` Source file found twice under different module names: "mypyc.lib-rt.build_setup" and "build_setup" ``` etc. This is kind of a known problem, that we need to address long-term.
1 parent 9c3ed87 commit 4fabef2

File tree

7 files changed

+97
-62
lines changed

7 files changed

+97
-62
lines changed

mypy/test/testmypyc.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@
22

33
from __future__ import annotations
44

5+
import filecmp
56
import os
67
from unittest import TestCase
78

89
import mypy
10+
import mypyc
911

1012

1113
class MypycTest(TestCase):
1214
def test_using_mypyc(self) -> None:
1315
if os.getenv("TEST_MYPYC", None) == "1":
1416
assert not mypy.__file__.endswith(".py"), "Expected to find a mypyc-compiled version"
17+
18+
def test_shared_files_consistent(self) -> None:
19+
if os.getenv("TEST_MYPYC", None) != "1":
20+
mypyc_path = mypyc.__path__[0]
21+
for f in ["build_setup.py"]:
22+
assert filecmp.cmp(
23+
os.path.join(mypyc_path, f),
24+
os.path.join(mypyc_path, "lib-rt", f),
25+
shallow=False,
26+
), f"Shared files inconsistent, run cp mypyc/{f} mypyc/lib-rt"

mypy_self_check.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pretty = True
88
always_false = MYPYC
99
plugins = mypy.plugins.proper_plugin
1010
python_version = 3.10
11-
exclude = mypy/typeshed/|mypyc/test-data/
11+
exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/
1212
enable_error_code = ignore-without-code,redundant-expr
1313
enable_incomplete_feature = PreciseTupleTypes
1414
show_error_code_links = True

mypyc/build_setup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# This file must have the same content for mypyc/build_setup.py and lib-rt/build_setup.py,
2+
# it exists to work around absence of support for per-file compile flags in setuptools.
3+
# The version in mypyc/ is the source of truth, and should be copied to lib-rt if modified.
4+
15
import os
26
import platform
37
import sys

mypyc/lib-rt/build_setup.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# This file must have the same content for mypyc/build_setup.py and lib-rt/build_setup.py,
2+
# it exists to work around absence of support for per-file compile flags in setuptools.
3+
# The version in mypyc/ is the source of truth, and should be copied to lib-rt if modified.
4+
5+
import os
6+
import platform
7+
import sys
8+
9+
try:
10+
# Import setuptools so that it monkey-patch overrides distutils
11+
import setuptools # noqa: F401
12+
except ImportError:
13+
pass
14+
15+
if sys.version_info >= (3, 12):
16+
# From setuptools' monkeypatch
17+
from distutils import ccompiler # type: ignore[import-not-found]
18+
else:
19+
from distutils import ccompiler
20+
21+
EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = {
22+
"unix": {
23+
"base64/arch/ssse3": ["-mssse3"],
24+
"base64/arch/sse41": ["-msse4.1"],
25+
"base64/arch/sse42": ["-msse4.2"],
26+
"base64/arch/avx2": ["-mavx2"],
27+
"base64/arch/avx": ["-mavx"],
28+
},
29+
"msvc": {
30+
"base64/arch/sse42": ["/arch:SSE4.2"],
31+
"base64/arch/avx2": ["/arch:AVX2"],
32+
"base64/arch/avx": ["/arch:AVX"],
33+
},
34+
}
35+
36+
ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined]
37+
X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64")
38+
PYODIDE = "PYODIDE" in os.environ
39+
40+
41+
def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def]
42+
if PYODIDE:
43+
new_cmd = list(cmd)
44+
for argument in reversed(new_cmd):
45+
if not str(argument).endswith(".c"):
46+
continue
47+
if "base64/arch/" in str(argument):
48+
new_cmd.extend(["-msimd128"])
49+
else:
50+
compiler_type: str = self.compiler_type
51+
extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type]
52+
new_cmd = list(cmd)
53+
if X86_64 and extra_options is not None:
54+
# filenames are closer to the end of command line
55+
for argument in reversed(new_cmd):
56+
# Check if the matching argument contains a source filename.
57+
if not str(argument).endswith(".c"):
58+
continue
59+
60+
for path in extra_options.keys():
61+
if path in str(argument):
62+
if compiler_type == "bcpp":
63+
compiler = new_cmd.pop()
64+
# Borland accepts a source file name at the end,
65+
# insert the options before it
66+
new_cmd.extend(extra_options[path])
67+
new_cmd.append(compiler)
68+
else:
69+
new_cmd.extend(extra_options[path])
70+
71+
# path component is found, no need to search any further
72+
break
73+
self.__spawn(new_cmd, **kwargs)
74+
75+
76+
ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign]

mypyc/lib-rt/setup.py

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
from __future__ import annotations
77

88
import os
9-
import platform
109
import subprocess
1110
import sys
1211
from distutils import ccompiler, sysconfig
1312
from typing import Any
1413

14+
import build_setup # noqa: F401
1515
from setuptools import Extension, setup
1616
from setuptools.command.build_ext import build_ext
1717

@@ -25,63 +25,6 @@
2525
"pythonsupport.c",
2626
]
2727

28-
EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = {
29-
"unix": {
30-
"base64/arch/ssse3": ["-mssse3"],
31-
"base64/arch/sse41": ["-msse4.1"],
32-
"base64/arch/sse42": ["-msse4.2"],
33-
"base64/arch/avx2": ["-mavx2"],
34-
"base64/arch/avx": ["-mavx"],
35-
},
36-
"msvc": {
37-
"base64/arch/sse42": ["/arch:SSE4.2"],
38-
"base64/arch/avx2": ["/arch:AVX2"],
39-
"base64/arch/avx": ["/arch:AVX"],
40-
},
41-
}
42-
43-
ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined]
44-
X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64")
45-
PYODIDE = "PYODIDE" in os.environ
46-
47-
48-
def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def]
49-
if PYODIDE:
50-
new_cmd = list(cmd)
51-
for argument in reversed(new_cmd):
52-
if not str(argument).endswith(".c"):
53-
continue
54-
if "base64/arch/" in str(argument):
55-
new_cmd.extend(["-msimd128"])
56-
else:
57-
compiler_type: str = self.compiler_type
58-
extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type]
59-
new_cmd = list(cmd)
60-
if X86_64 and extra_options is not None:
61-
# filenames are closer to the end of command line
62-
for argument in reversed(new_cmd):
63-
# Check if the matching argument contains a source filename.
64-
if not str(argument).endswith(".c"):
65-
continue
66-
67-
for path in extra_options.keys():
68-
if path in str(argument):
69-
if compiler_type == "bcpp":
70-
compiler = new_cmd.pop()
71-
# Borland accepts a source file name at the end,
72-
# insert the options before it
73-
new_cmd.extend(extra_options[path])
74-
new_cmd.append(compiler)
75-
else:
76-
new_cmd.extend(extra_options[path])
77-
78-
# path component is found, no need to search any further
79-
break
80-
self.__spawn(new_cmd, **kwargs)
81-
82-
83-
ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign]
84-
8528

8629
class BuildExtGtest(build_ext):
8730
def get_library_names(self) -> list[str]:
@@ -130,13 +73,11 @@ def run(self) -> None:
13073
cmdclass={"build_ext": BuildExtGtest},
13174
)
13275
else:
133-
# TODO: we need a way to share our preferred C flags and get_extension() logic with
134-
# mypyc/build.py without code duplication.
13576
compiler = ccompiler.new_compiler()
13677
sysconfig.customize_compiler(compiler)
13778
cflags: list[str] = []
13879
if compiler.compiler_type == "unix": # type: ignore[attr-defined]
139-
cflags += ["-O3"]
80+
cflags += ["-O3", "-Wno-unused-function"]
14081
elif compiler.compiler_type == "msvc": # type: ignore[attr-defined]
14182
cflags += ["/O2"]
14283

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ def run(self) -> None:
9898
) + (
9999
# Don't want to grab this accidentally
100100
os.path.join("mypyc", "lib-rt", "setup.py"),
101+
os.path.join("mypyc", "lib-rt", "build_setup.py"),
101102
# Uses __file__ at top level https://github.com/mypyc/mypyc/issues/700
102103
os.path.join("mypyc", "__main__.py"),
103104
os.path.join("mypyc", "build_setup.py"), # for monkeypatching

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ commands =
6262
python runtests.py self
6363
python -m mypy --config-file mypy_self_check.ini misc --exclude misc/sync-typeshed.py
6464
python -m mypy --config-file mypy_self_check.ini test-data/unit/plugins
65+
python -m mypy mypyc/lib-rt

0 commit comments

Comments
 (0)