| -- These test cases compile two or more modules at a time. |
| -- Any file prefixed with "other" is compiled. |
| -- |
| -- Note that these are run in three compilation modes: regular, |
| -- multi-file and separate. See the docstrings of |
| -- mypyc.test.test_run.TestRunMultiFile and |
| -- mypyc.test.test_run.TestRunSeparate for more information. |
| -- |
| -- Some of these files perform multiple incremental runs. See |
| -- test-data/unit/check-incremental.test for more information |
| -- about how this is specified (e.g. .2 file name suffixes). |
| |
| [case testMultiModulePackage] |
| from p.other import g, _i as i |
| def f(x: int) -> int: |
| from p.other import h |
| return i(h(g(x + 1))) |
| [file p/__init__.py] |
| [file p/other.py] |
| def g(x: int) -> int: |
| return x + 2 |
| def h(x: int) -> int: |
| return x + 1 |
| def _i(x: int) -> int: |
| return x + 3 |
| [file driver.py] |
| import native |
| from native import f |
| from p.other import g |
| assert f(3) == 10 |
| assert g(2) == 4 |
| try: |
| f(1.1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| try: |
| g(1.1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| [case testImportNonNativeSubmoduleFrom] |
| # separate: [(["native.py", "other_pkg/__init__.py"], "testgroup")] |
| import types |
| from other_pkg import helper |
| |
| def f() -> int: |
| return helper.func() |
| |
| def g() -> types.ModuleType: |
| return helper |
| [file other_pkg/__init__.py] |
| [file other_pkg/helper.py] |
| def func() -> int: |
| return 42 |
| [file driver.py] |
| from native import f, g |
| import other_pkg.helper |
| assert f() == 42 |
| assert g() is other_pkg.helper |
| |
| [case testNativeSubmoduleOfNonNativePackageFrom] |
| # separate: [(["native.py", "pkg/other_mod.py"], "testgroup")] |
| from pkg import other_mod |
| |
| def f() -> int: |
| return other_mod.func() |
| [file pkg/__init__.py] |
| [file pkg/other_mod.py] |
| def func() -> int: |
| return 42 |
| [file driver.py] |
| import importlib.machinery |
| import os |
| import native |
| import pkg.other_mod |
| assert native.f() == 42 |
| # other_mod should be in native's namespace and be the same object as pkg.other_mod |
| assert native.other_mod is pkg.other_mod |
| # native-to-native import should preserve module metadata such as __file__ |
| ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES |
| assert any(native.other_mod.__file__.endswith(s) for s in ext_suffixes), native.other_mod.__file__ |
| # The directory of __file__ should reflect the package structure relative to the root |
| root_dir = os.path.dirname(native.__file__) |
| assert os.path.dirname(native.other_mod.__file__) == os.path.join(root_dir, "pkg"), \ |
| native.other_mod.__file__ |
| |
| [case testNativeSubmoduleOfNativePackageFrom] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| from other_pkg import other_sub |
| |
| def f() -> int: |
| return other_sub.func() |
| [file other_pkg/__init__.py] |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 42 |
| [file driver.py] |
| import importlib.machinery |
| import os |
| import native |
| import other_pkg.other_sub |
| assert native.f() == 42 |
| assert native.other_sub is other_pkg.other_sub |
| # Fast path import should preserve __file__ as a proper extension path |
| ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES |
| assert any(native.other_sub.__file__.endswith(s) for s in ext_suffixes), native.other_sub.__file__ |
| # The directory of __file__ should reflect the package structure relative to the root |
| root_dir = os.path.dirname(native.__file__) |
| assert os.path.dirname(native.other_sub.__file__) == os.path.join(root_dir, "other_pkg"), \ |
| native.other_sub.__file__ |
| |
| [case testImportFromBucketingEndToEnd] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| from other_pkg import other_sub as sub_alias, other_attr as attr_alias, py_sub as py_alias |
| [file other_pkg/__init__.py] |
| other_attr = 41 |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 10 |
| [file other_pkg/py_sub.py] |
| def func() -> int: |
| return 20 |
| [file driver.py] |
| import importlib |
| import native |
| other_sub = importlib.import_module("other_pkg.other_sub") |
| py_sub = importlib.import_module("other_pkg.py_sub") |
| assert native.sub_alias is other_sub |
| assert native.attr_alias == 41 |
| assert native.py_alias is py_sub |
| assert native.sub_alias.func() == 10 |
| assert native.py_alias.func() == 20 |
| |
| [case testNativeTopLevelModuleFileDir] |
| # separate: [(["native.py", "other_top.py"], "testgroup")] |
| import other_top |
| |
| def f() -> int: |
| return other_top.func() |
| [file other_top.py] |
| def func() -> int: |
| return 99 |
| [file driver.py] |
| import importlib.machinery |
| import os |
| import native |
| import other_top |
| assert native.f() == 99 |
| # Top-level native module __file__ directory should match the shared lib's directory |
| ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES |
| assert any(other_top.__file__.endswith(s) for s in ext_suffixes), other_top.__file__ |
| root_dir = os.path.dirname(native.__file__) |
| assert os.path.dirname(other_top.__file__) == root_dir, other_top.__file__ |
| |
| [case testNativeDeeplyNestedSubmoduleFileDir] |
| # separate: [(["native.py", "other_a/__init__.py", "other_a/other_b/__init__.py", "other_a/other_b/other_c.py"], "testgroup")] |
| from other_a.other_b import other_c |
| |
| def f() -> int: |
| return other_c.func() |
| [file other_a/__init__.py] |
| [file other_a/other_b/__init__.py] |
| [file other_a/other_b/other_c.py] |
| def func() -> int: |
| return 77 |
| [file driver.py] |
| import importlib.machinery |
| import os |
| import native |
| import other_a.other_b.other_c |
| assert native.f() == 77 |
| assert native.other_c is other_a.other_b.other_c |
| # Deeply nested module __file__ should have correct extension suffix |
| ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES |
| assert any(native.other_c.__file__.endswith(s) for s in ext_suffixes), native.other_c.__file__ |
| # The directory should reflect the full package hierarchy |
| root_dir = os.path.dirname(native.__file__) |
| expected_dir = os.path.join(root_dir, "other_a", "other_b") |
| assert os.path.dirname(native.other_c.__file__) == expected_dir, native.other_c.__file__ |
| |
| [case testNativeImportPackageAttribute] |
| # separate: [(["native.py", "other_top.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| import other_top |
| from other_pkg import other_sub |
| import other_pkg |
| |
| def get_top() -> str: |
| p = other_top.__package__ |
| assert p is not None |
| return p |
| |
| def get_sub() -> str: |
| p = other_sub.__package__ |
| assert p is not None |
| return p |
| |
| def get_pkg() -> str: |
| p = other_pkg.__package__ |
| assert p is not None |
| return p |
| [file other_top.py] |
| def func() -> int: |
| return 1 |
| [file other_pkg/__init__.py] |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 2 |
| [file driver.py] |
| from native import get_top, get_sub, get_pkg |
| |
| # Top-level module: __package__ should be "" (empty string) |
| assert get_top() == "", repr(get_top()) |
| |
| # Submodule: __package__ should be the parent package name |
| assert get_sub() == "other_pkg", repr(get_sub()) |
| |
| # Package __init__: __package__ should be the package name itself |
| assert get_pkg() == "other_pkg", repr(get_pkg()) |
| |
| [case testNativeImportFileAvailableAtInitTime] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| from other_pkg import other_sub |
| |
| def f() -> int: |
| return other_sub.func() |
| [file other_pkg/__init__.py] |
| [file other_pkg/other_sub.py] |
| # __file__ should be available during module initialization |
| _file: str = __file__ |
| |
| def func() -> int: |
| return 42 |
| [file driver.py] |
| import importlib.machinery |
| import native |
| assert native.f() == 42 |
| # Verify that __file__ captured at init time has a valid extension suffix |
| ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES |
| assert any(native.other_sub._file.endswith(s) for s in ext_suffixes), native.other_sub._file |
| |
| [case testNativeImportPathAttribute] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| import other_pkg |
| from other_pkg import other_sub |
| |
| def get_pkg_path() -> str: |
| return other_pkg.__path__[0] |
| |
| def get_sub_has_path() -> bool: |
| return hasattr(other_sub, '__path__') |
| [file other_pkg/__init__.py] |
| # __path__ should be available during package initialization |
| _path: list[str] = __path__ |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 1 |
| [file driver.py] |
| import os |
| import native |
| |
| # Package should have __path__ set to [dirname(__file__)] |
| expected = os.path.dirname(native.other_pkg.__file__) |
| assert native.get_pkg_path() == expected, (native.get_pkg_path(), expected) |
| |
| # __path__ should have been accessible at init time |
| assert native.other_pkg._path == [expected], native.other_pkg._path |
| |
| # Non-package submodule should NOT have __path__ |
| assert not native.get_sub_has_path() |
| |
| [case testNativeImportPackageFileDirMatchesPath] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| import other_pkg |
| from other_pkg import other_sub |
| |
| def get_pkg_file() -> str: |
| return other_pkg.__file__ |
| |
| def get_pkg_path() -> str: |
| return other_pkg.__path__[0] |
| |
| def get_sub_file() -> str: |
| return other_sub.__file__ |
| [file other_pkg/__init__.py] |
| x = 1 |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 1 |
| [file driver.py] |
| import os |
| import native |
| |
| pkg_file = native.get_pkg_file() |
| pkg_path = native.get_pkg_path() |
| sub_file = native.get_sub_file() |
| |
| # Package __file__ should contain __init__ (e.g. other_pkg/__init__.cpython-312-x86_64-linux-gnu.so) |
| assert "__init__" in os.path.basename(pkg_file), \ |
| f"Package __file__ should contain __init__, got: {pkg_file}" |
| |
| # Package __path__ should be the directory containing the package's __init__ file |
| assert pkg_path == os.path.dirname(pkg_file), \ |
| f"__path__ ({pkg_path}) should equal dirname(__file__) ({os.path.dirname(pkg_file)})" |
| |
| # __path__ should end with the package name |
| assert os.path.basename(pkg_path) == "other_pkg", \ |
| f"__path__ should end with package name, got: {pkg_path}" |
| |
| # Submodule __file__ should be inside the package directory (i.e. inside __path__) |
| assert os.path.dirname(sub_file) == pkg_path, \ |
| f"Submodule dir ({os.path.dirname(sub_file)}) should equal package __path__ ({pkg_path})" |
| |
| [case testNativeImportSpecAttribute] |
| # separate: [(["native.py", "other_top.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| from typing import Any |
| import other_top |
| from other_pkg import other_sub |
| import other_pkg |
| |
| def get_top_spec() -> Any: |
| return other_top.__spec__ |
| |
| def get_sub_spec() -> Any: |
| return other_sub.__spec__ |
| |
| def get_pkg_spec() -> Any: |
| return other_pkg.__spec__ |
| [file other_top.py] |
| from typing import Any |
| # __spec__ should be available during module initialization |
| _spec: Any = __spec__ # type: ignore[name-defined] |
| |
| def func() -> int: |
| return 1 |
| [file other_pkg/__init__.py] |
| [file other_pkg/other_sub.py] |
| def func() -> int: |
| return 2 |
| [file driver.py] |
| import importlib.machinery |
| import native |
| |
| top_spec = native.get_top_spec() |
| sub_spec = native.get_sub_spec() |
| pkg_spec = native.get_pkg_spec() |
| |
| # __spec__.name should match the module name |
| assert top_spec.name == "other_top" |
| assert sub_spec.name == "other_pkg.other_sub" |
| assert pkg_spec.name == "other_pkg" |
| |
| # __spec__ should have correct origin (same as __file__) |
| assert top_spec.origin == native.other_top.__file__ |
| assert sub_spec.origin == native.other_sub.__file__ |
| |
| # __spec__.loader should be an ExtensionFileLoader and also be set as __loader__ |
| assert isinstance(top_spec.loader, importlib.machinery.ExtensionFileLoader) |
| assert native.other_top.__loader__ is top_spec.loader |
| |
| # Package spec should have submodule_search_locations |
| assert pkg_spec.submodule_search_locations is not None |
| |
| # Non-package spec should not have submodule_search_locations |
| assert top_spec.submodule_search_locations is None |
| |
| # __spec__ should be available at init time |
| assert native.other_top._spec.name == "other_top" |
| |
| [case testNativeSubmoduleFailureDoesNotBindParentEarly] |
| # separate: [(["native.py", "other_pkg/__init__.py", "other_pkg/other_sub.py"], "testgroup")] |
| from other_pkg import other_sub |
| [file other_pkg/__init__.py] |
| x = 1 |
| [file other_pkg/other_sub.py] |
| import other_pkg |
| |
| if hasattr(other_pkg, "other_sub"): |
| raise RuntimeError("parent attr visible too early") |
| raise RuntimeError("boom from other_sub") |
| [file driver.py] |
| import sys |
| import other_pkg |
| |
| try: |
| import native |
| except RuntimeError as e: |
| assert str(e) == "boom from other_sub" |
| else: |
| assert False, "import native should fail" |
| |
| assert not hasattr(other_pkg, "other_sub") |
| assert "other_pkg.other_sub" not in sys.modules |
| |
| [case testNativeImportUsesExistingNativeSysModulesObject] |
| # separate: [(["native.py", "other_top.py"], "testgroup")] |
| import other_top |
| |
| def f() -> int: |
| return other_top.value |
| [file other_top.py] |
| value = 42 |
| [file driver.py] |
| import native |
| import other_top |
| |
| assert native.other_top is other_top |
| assert native.f() == 42 |
| |
| [case testNativeImportRejectsForeignSysModulesObject] |
| # separate: [(["native.py", "other_top.py"], "testgroup")] |
| import other_top |
| |
| def f() -> int: |
| return other_top.value |
| [file other_top.py] |
| value = 42 |
| [file driver.py] |
| import sys |
| import types |
| |
| import other_top |
| |
| fake = types.ModuleType("other_top") |
| fake.value = -1 |
| sys.modules["other_top"] = fake |
| |
| try: |
| import native |
| except ImportError as e: |
| assert "other_top" in str(e) |
| assert "replaced after initialization" in str(e) |
| else: |
| assert False, "import native should fail" |
| |
| assert sys.modules["other_top"] is fake |
| |
| [case testMultiModuleFastpaths] |
| [file other_main.py] |
| |
| [file other_main.py.2] |
| from other_b import A, func |
| |
| class B(A): |
| pass |
| |
| def test() -> None: |
| a = A() |
| assert func() == 12 |
| assert a.method() == "test" |
| |
| test() |
| |
| [file other_b.py] |
| class A: |
| def method(self) -> str: |
| return "test" |
| |
| def func() -> int: |
| return 12 |
| |
| # Remove all the methods and functions from globals to ensure that |
| # they get called via the fastpaths even when doing incremental |
| # compilation. |
| setattr(A, 'method', None) |
| setattr(A, '__init__', None) |
| globals()['func'] = None |
| globals()['A'] = None |
| |
| [file driver.py] |
| import other_main |
| |
| [case testNonNativeImportInPackageFile] |
| # The import is really non-native only in separate compilation mode where __init__.py and |
| # other_cache.py are in different libraries and the import uses the standard Python procedure. |
| # Python imports are resolved using __path__ and __spec__ from the package file so this checks |
| # that they are set up correctly. |
| [file other/__init__.py] |
| from other.other_cache import Cache |
| |
| x = 1 |
| [file other/other_cache.py] |
| class Cache: |
| pass |
| |
| [file driver.py] |
| import other |
| |
| [case testRelativeImportInPackageFile] |
| # Relative imports from a compiled package __init__ depend on package metadata being |
| # available while the package module body is executing. |
| [file other/__init__.py] |
| assert __package__ == "other" |
| from .other_cache import Cache |
| |
| x = 1 |
| [file other/other_cache.py] |
| class Cache: |
| pass |
| |
| [file driver.py] |
| import other |
| assert other.Cache.__name__ == "Cache" |
| |
| [case testMultiModuleSameNames] |
| # Use same names in both modules |
| import other |
| def f() -> int: |
| return 0 |
| class C: |
| x: int |
| def __init__(self) -> None: |
| self.x = 1 |
| def f(self, x: int) -> int: |
| return self.x + x |
| class D(C): pass |
| |
| def g(x: 'other.C') -> None: |
| pass |
| |
| [file other.py] |
| def f(x: int) -> int: |
| return x + 1 |
| class C: |
| x: int |
| def __init__(self) -> None: |
| self.x = 2 |
| def f(self, x: int) -> int: |
| return self.x + x + 1 |
| class D(C): pass |
| [file driver.py] |
| import native, other |
| assert native.f() == 0 |
| assert other.f(3) == 4 |
| c1 = native.C() |
| c1.x += 3 |
| c2 = other.C() |
| c2.x += 6 |
| assert c1.f(9) == 1 + 3 + 9 |
| assert c2.f(7) == 2 + 6 + 7 + 1 |
| assert isinstance(native.D(), native.C) |
| assert isinstance(other.D(), other.C) |
| assert not isinstance(native.D(), other.C) |
| assert not isinstance(other.D(), native.C) |
| |
| [case testMultiModuleInitializeImportedModules] |
| from other import f |
| |
| def g() -> int: |
| return f(1) |
| [file other.py] |
| def f(x: int) -> int: |
| return x + 4 |
| [file driver.py] |
| import sys |
| assert 'other' not in sys.modules |
| from native import g |
| assert 'other' in sys.modules |
| assert g() == 5 |
| f = sys.modules['other'].f |
| assert f(1) == 5 |
| try: |
| f(1.1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| [case testMultiModuleImportClass] |
| from typing import cast |
| from other import C, a_global |
| |
| class D(C): |
| def __init__(self, x: int) -> None: |
| self.x = x |
| |
| def f(c: C) -> int: |
| d = D(3) |
| o: object = c |
| c = cast(C, o) |
| return a_global + c.x + c.f() + d.x + d.f() + 1 |
| [file other.py] |
| from typing import Final |
| a_global: Final = int('5') |
| |
| class C: |
| x: int |
| |
| def __init__(self, x: int) -> None: |
| self.x = x |
| |
| def __hash__(self) -> int: |
| return self.x |
| |
| def __str__(self) -> str: |
| return str(self.x) |
| |
| def f(self) -> int: |
| return 2 |
| |
| def check(self) -> None: |
| assert isinstance(self, C) |
| |
| [file driver.py] |
| from native import f, D |
| from other import C |
| c = C(4) |
| assert f(c) == 5 + 4 + 2 + 3 + 2 + 1 |
| assert str(D(10)) == '10' |
| assert hash(10) == 10 |
| try: |
| f(1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| assert isinstance(D(10), C) |
| |
| c.check() |
| D(10).check() |
| |
| [case testMultiModuleSpecialize] |
| from other import A |
| |
| class B(A): |
| def foo(self, x: object) -> int: |
| print(2) |
| return id(x) |
| [file other.py] |
| class A: |
| def foo(self, x: int) -> object: |
| print(1) |
| return str(x) |
| |
| def use_a(x: A, y: int) -> object: |
| return x.foo(y) |
| |
| [file driver.py] |
| from native import B |
| from other import A, use_a |
| a = A() |
| b = B() |
| o = object() |
| i = 10 |
| assert a.foo(10) == '10' |
| assert b.foo(o) == id(o) |
| assert use_a(a, 10) == '10' |
| assert use_a(b, i) == id(i) |
| [out] |
| 1 |
| 2 |
| 1 |
| 2 |
| |
| [case testMultiModuleLiterals] |
| from other import gs, gi, gf |
| |
| def fs() -> str: |
| return 'f' + gs() |
| def fi() -> int: |
| return 10001000100010001000 + gi() |
| def ff() -> float: |
| return 2.0 + gf() |
| [file other.py] |
| def gi() -> int: |
| return 20001000100010001000 |
| def gs() -> str: |
| return 'g' |
| def gf() -> float: |
| return 3.0 |
| [file driver.py] |
| from native import fs, fi, ff |
| assert fs() == 'fg' |
| assert fi() == 30002000200020002000 |
| assert ff() == 5.0 |
| |
| [case testMultiModuleTraceback] |
| from other import fail2 |
| |
| def fail() -> None: |
| fail2() |
| [file other.py] |
| def fail2() -> None: |
| x = [1] |
| x[2] = 2 |
| [file driver.py] |
| import traceback |
| import sys |
| import native |
| import other |
| try: |
| other.fail2() |
| except IndexError: |
| tb = sys.exc_info()[2] |
| assert tb.tb_next.tb_frame.f_globals is other.__dict__ |
| traceback.print_exc() |
| try: |
| native.fail() |
| except IndexError: |
| tb = sys.exc_info()[2] |
| assert tb.tb_next.tb_frame.f_globals is native.__dict__ |
| traceback.print_exc() |
| [out] |
| Traceback (most recent call last): |
| File "driver.py", line 6, in <module> |
| other.fail2() |
| File "other.py", line 3, in fail2 |
| x[2] = 2 |
| IndexError: list assignment index out of range |
| Traceback (most recent call last): |
| File "driver.py", line 12, in <module> |
| native.fail() |
| File "native.py", line 4, in fail |
| fail2() |
| File "other.py", line 3, in fail2 |
| x[2] = 2 |
| IndexError: list assignment index out of range |
| [out version>=3.13] |
| Traceback (most recent call last): |
| File "driver.py", line 6, in <module> |
| other.fail2() |
| ~~~~~~~~~~~^^ |
| File "other.py", line 3, in fail2 |
| x[2] = 2 |
| IndexError: list assignment index out of range |
| Traceback (most recent call last): |
| File "driver.py", line 12, in <module> |
| native.fail() |
| ~~~~~~~~~~~^^ |
| File "native.py", line 4, in fail |
| fail2() |
| File "other.py", line 3, in fail2 |
| x[2] = 2 |
| IndexError: list assignment index out of range |
| |
| [case testMultiModuleCycle] |
| if False: |
| from typing import Final |
| import other |
| |
| x = int('0') # type: Final |
| |
| def f1() -> int: |
| return other.f2() + other.x |
| |
| def f3() -> int: |
| return 5 |
| [file other.py] |
| if False: |
| from typing import Final |
| import native |
| |
| x = int('0') # type: Final |
| |
| def f2() -> int: |
| return native.f3() + native.x |
| [file driver.py] |
| from native import f1 |
| assert f1() == 5 |
| |
| [case testMultiModuleCycleWithClasses] |
| import other |
| |
| class D: pass |
| |
| def f() -> other.C: |
| return other.C() |
| |
| def g(c: other.C) -> D: |
| return c.d |
| |
| [file other.py] |
| import native |
| |
| class C: |
| def __init__(self) -> None: |
| self.d = native.D() |
| |
| def h(d: native.D) -> None: |
| pass |
| |
| [file driver.py] |
| from native import f, g |
| from other import C, h |
| |
| c = f() |
| assert isinstance(c, C) |
| assert g(c) is c.d |
| h(c.d) |
| |
| try: |
| g(1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| try: |
| h(1) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| [case testMultiModuleCycleWithInheritance] |
| import other |
| |
| class Deriv1(other.Base1): |
| def __init__(self) -> None: |
| super().__init__() |
| |
| class Base2: |
| y: int |
| def __init__(self) -> None: |
| self.y = 2 |
| |
| [file other.py] |
| from typing import Tuple |
| import native |
| |
| class Base1: |
| a: Tuple[int, int] |
| x: int |
| def __init__(self) -> None: |
| self.x = 1 |
| |
| def make_2() -> native.Base2: |
| return native.Base2() |
| |
| [file driver.py] |
| from native import Deriv1 |
| from other import make_2 |
| a = Deriv1() |
| assert a.x == 1 |
| b = make_2() |
| assert b.y == 2 |
| |
| [case testMultiModuleTraitInheritance] |
| from other import Base1, Base2 |
| |
| class Deriv1(Base1, Base2): |
| pass |
| |
| [file other.py] |
| from mypy_extensions import trait |
| |
| @trait |
| class Base1: |
| def foo(self) -> int: return 10 |
| @trait |
| class Base2: |
| def bar(self) -> int: return 12 |
| |
| [file driver.py] |
| from native import Deriv1 |
| a = Deriv1() |
| assert a.foo() == 10 and a.bar() == 12 |
| |
| |
| [case testImportCycleWithNonCompiledModule] |
| import m |
| |
| class C: pass |
| |
| def f1() -> int: |
| m.D() |
| return m.f2() |
| |
| def f3() -> int: |
| return 2 |
| |
| [file m.py] |
| # This module is NOT compiled |
| import native |
| |
| class D: pass |
| |
| def f2() -> int: |
| native.C() |
| return native.f3() |
| |
| [file driver.py] |
| from native import f1 |
| |
| assert f1() == 2 |
| |
| [case testImportCycleWithTopLevelStatements] |
| import other |
| x = 1 |
| print(x) |
| |
| [file other.py] |
| import native |
| x = 2 |
| print(x) |
| |
| [file driver.py] |
| import other |
| print('-') |
| import native |
| print('>', native.x) |
| print('>', other.x) |
| |
| [out] |
| 1 |
| 2 |
| - |
| > 1 |
| > 2 |
| |
| [case testMultiModuleCycleIfMypy1] |
| from other import foo, bar |
| |
| class Foo: |
| def foo(self) -> None: |
| foo(self) |
| class Bar: |
| def bar(self) -> None: |
| bar(self) |
| |
| [file other.py] |
| from typing import TYPE_CHECKING |
| MYPY = False |
| if MYPY: |
| from native import Foo |
| if TYPE_CHECKING: |
| from native import Bar |
| |
| def foo(x: 'Foo') -> None: |
| pass |
| def bar(x: 'Bar') -> None: |
| pass |
| |
| [file driver.py] |
| from native import Foo, Bar |
| Foo().foo() |
| Bar().bar() |
| |
| [case testMultiModuleCycleIfMypy2] |
| MYPY = False |
| if MYPY: |
| from other import C |
| |
| class D: |
| def __init__(self) -> None: |
| self.y = 1 |
| |
| def f(c: 'C') -> int: |
| return c.x |
| |
| [file other.py] |
| from typing import TYPE_CHECKING |
| if TYPE_CHECKING: |
| from native import D |
| |
| class C: |
| def __init__(self) -> None: |
| self.x = 2 |
| |
| def g(d: 'D') -> int: |
| return d.y |
| |
| [file driver.py] |
| from native import f, D |
| from other import g, C |
| |
| assert f(C()) == 2 |
| assert g(D()) == 1 |
| |
| try: |
| f(D()) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| try: |
| g(C()) |
| except TypeError: |
| pass |
| else: |
| assert False |
| |
| [case testMultiModuleRelative] |
| from package.a import f |
| [file package/__init__.py] |
| [file package/a.py] |
| from . import b |
| from .c import c3 |
| def f() -> None: |
| print("Hello " + b.b2()) |
| print("Hello " + c3()) |
| [file package/b.py] |
| def b2() -> str: |
| return "moon!" |
| [file package/c.py] |
| def c3() -> str: |
| return "sun!" |
| |
| [file driver.py] |
| from native import f |
| f() |
| [out] |
| Hello moon! |
| Hello sun! |
| |
| [case testMultiModuleCrash] |
| b = False |
| if b: |
| import other |
| |
| def foo() -> None: |
| try: |
| other.x |
| except: |
| pass |
| else: |
| assert False |
| |
| [file other.py] |
| x = 10 |
| |
| [file driver.py] |
| from native import foo |
| foo() |
| |
| [case testTrivialIncremental] |
| # separate: [(["other.py", "other_b.py"], "stuff")] |
| from other import x |
| from other_b import z |
| y = x + z |
| [file other.py] |
| x = 1 |
| [file other.py.2] |
| x = 2 |
| [file other_b.py] |
| z = 1 |
| |
| [file driver.py] |
| from native import y |
| print(y) |
| [out] |
| 2 |
| [out2] |
| 3 |
| [rechecked other, other_b] |
| |
| [case testIncrementalCompilation1] |
| import non_native |
| from other_a import A |
| from other_b import z |
| |
| a = A() |
| assert a.y == z |
| |
| assert non_native.foo() == 0 |
| |
| [file other_a.py] |
| from other_b import z |
| from typing import Iterable |
| |
| class A: |
| def __init__(self) -> None: |
| self.y = z |
| [file other_a.py.2] |
| from other_b import z |
| from typing import Iterable |
| |
| class A: |
| def __init__(self) -> None: |
| self.x = 'test' |
| self.y = z |
| [file other_b.py] |
| import other_a |
| |
| z = 10 |
| |
| def foo() -> 'other_a.A': |
| return other_a.A() |
| [file other_b.py.3] |
| import other_a |
| |
| z = 20 |
| |
| def foo() -> 'other_a.A': |
| return other_a.A() |
| |
| [file non_native.py] |
| import other_a |
| |
| def foo() -> int: |
| return 0 |
| |
| [file non_native.py.4] |
| import other_a |
| |
| def foo() -> float: |
| return 0 |
| |
| [file driver.py] |
| from native import a |
| print(a.y, getattr(a, 'x', None)) |
| |
| [out] |
| 10 None |
| [out2] |
| 10 test |
| [out3] |
| 20 test |
| [out4] |
| 20 test |
| |
| [rechecked other_a, other_b, native, non_native] |
| [rechecked2 other_a, other_b] |
| [rechecked3 native, non_native] |
| |
| |
| -- This one tests a group that is not an SCC. |
| [case testIncrementalCompilation2] |
| # separate: [(["other_a.py", "other_b.py"], "stuff")] |
| from other_a import A |
| from other_b import z |
| |
| a = A() |
| assert a.y == z |
| |
| [file other_a.py] |
| from other_b import z |
| |
| class A: |
| def __init__(self) -> None: |
| self.y = z |
| [file other_a.py.2] |
| from other_b import z |
| |
| class A: |
| def __init__(self) -> None: |
| self.x = 'test' |
| self.y = z |
| |
| [file other_b.py] |
| z = 10 |
| |
| [file driver.py] |
| from native import a |
| print(a.y, getattr(a, 'x', None)) |
| |
| [out] |
| 10 None |
| [out2] |
| 10 test |
| |
| [rechecked other_a, other_b, native] |
| |
| [case testIncrementalCompilation3] |
| from other import X |
| Y = X |
| def foo() -> int: |
| return X |
| |
| [file other.py] |
| from typing import Final |
| X: Final = 10 |
| |
| [file other.py.2] |
| from typing import Final |
| X: Final = 20 |
| |
| [file driver.py] |
| import native |
| import other |
| assert native.Y == other.X |
| assert native.foo() == other.X |
| |
| [rechecked native, other] |
| |
| -- This one tests a group changing |
| [case testIncrementalCompilation4] |
| # separate: [(["other_a.py", "other_b.py"], "stuff")] |
| # separate2: [] |
| from other_a import A |
| from other_b import z |
| |
| a = A() |
| assert a.y == z |
| |
| [file other_a.py] |
| from other_b import z |
| |
| class A: |
| def __init__(self) -> None: |
| self.y = z |
| |
| [file other_b.py] |
| z = 10 |
| |
| [file wtvr.py.2] |
| |
| [file driver.py] |
| from native import a |
| print(a.y, getattr(a, 'x', None)) |
| |
| [out] |
| 10 None |
| [out2] |
| 10 None |
| |
| [rechecked other_a, other_b, native] |
| |
| -- This one tests cases where other modules *do not* need rechecked |
| [case testIncrementalCompilation5] |
| import other_a |
| [file other_a.py] |
| from other_b import f |
| assert f(10) == 20 |
| [file other_a.py.2] |
| from other_b import f |
| assert f(20) == 40 |
| |
| [file other_b.py] |
| def f(x: int) -> int: |
| return x * 2 |
| |
| [file driver.py] |
| import native |
| |
| [rechecked other_a] |
| |
| -- Delete one of the C files and make sure this forces recompilation |
| [case testIncrementalCompilation6] |
| import other_a |
| assert other_a.foo() == 10 |
| [file other_a.py] |
| def foo() -> int: return 10 |
| |
| [file build/__native_other_a.c] |
| |
| [delete build/__native_other_a.c.2] |
| |
| [file driver.py] |
| import native |
| |
| [rechecked other_a] |
| |
| [case testSeparateCompilationWithUndefinedAttribute] |
| from other_a import A |
| |
| def f() -> None: |
| a = A() |
| if a.x == 5: |
| print(a.y) |
| print(a.m()) |
| else: |
| assert a.x == 6 |
| try: |
| print(a.y) |
| except AttributeError: |
| print('y undefined') |
| else: |
| assert False |
| |
| try: |
| print(a.m()) |
| except AttributeError: |
| print('y undefined') |
| else: |
| assert False |
| |
| [file other_a.py] |
| from other_b import B |
| |
| class A(B): |
| def __init__(self) -> None: |
| self.y = 9 |
| |
| [file other_a.py.2] |
| from other_b import B |
| |
| class A(B): |
| x = 6 |
| |
| def __init__(self) -> None: |
| pass |
| |
| [file other_b.py] |
| class B: |
| x = 5 |
| |
| def __init__(self) -> None: |
| self.y = 7 |
| |
| def m(self) -> int: |
| return self.y |
| |
| [file driver.py] |
| from native import f |
| f() |
| |
| [rechecked native, other_a] |
| |
| [out] |
| 9 |
| 9 |
| [out2] |
| y undefined |
| y undefined |
| |
| [case testIncrementalCompilationWithDeletable] |
| import other_a |
| [file other_a.py] |
| from other_b import C |
| [file other_a.py.2] |
| from other_b import C |
| c = C() |
| print(getattr(c, 'x', None)) |
| del c.x |
| print(getattr(c, 'x', None)) |
| [file other_b.py] |
| class C: |
| __deletable__ = ['x'] |
| def __init__(self) -> None: |
| self.x = 0 |
| [file driver.py] |
| import native |
| [out] |
| [out2] |
| 0 |
| None |
| |
| [case testIncrementalCompilationWithNonClassTypeDef] |
| import other_a |
| [file other_a.py] |
| from other_b import MyInt |
| [file other_a.py.2] |
| from other_b import MyInt, NT, TD |
| i = MyInt(42) |
| |
| def f(x: MyInt) -> int: |
| return x + 1 |
| |
| def g(x: int) -> MyInt: |
| return MyInt(x + 2) |
| |
| print(i) |
| print(f(i)) |
| print(g(13)) |
| |
| def make_nt(x: int) -> NT: |
| return NT(x=MyInt(x)) |
| |
| print(make_nt(4)) |
| |
| def make_td(x: int) -> TD: |
| return {"x": MyInt(x)} |
| |
| print(make_td(5)) |
| |
| [file other_b.py] |
| from typing import NewType, NamedTuple, TypedDict |
| from enum import Enum |
| |
| MyInt = NewType("MyInt", int) |
| NT = NamedTuple("NT", [("x", MyInt)]) |
| TD = TypedDict("TD", {"x": MyInt}) |
| |
| [file driver.py] |
| import native |
| |
| [typing fixtures/typing-full.pyi] |
| [out] |
| [out2] |
| 42 |
| 43 |
| 15 |
| NT(x=4) |
| {'x': 5} |
| |
| [case testExtraLibRtSourceFileDep] |
| table_list = list(range(256)) |
| table_list[ord('A')] = ord('B') |
| table = bytes(table_list) |
| |
| def translate(b: bytes) -> bytes: |
| # The primimitive for bytes.translate requires an optional C file from lib-rt |
| return b.translate(table) |
| [file driver.py] |
| import native |
| assert native.translate(b'ABCD') == b'BBCD' |
| |
| [case testCrossModuleAttrDefaults] |
| from other import Parent |
| |
| class Child(Parent): |
| extra: int = 99 |
| |
| def test() -> None: |
| c = Child() |
| assert c.config == {"key": "value"} |
| assert c.extra == 99 |
| assert c.total == 60 |
| p = Parent() |
| assert p.config == {"key": "value"} |
| assert p.total == 60 |
| assert Parent.TAG == "parent" |
| |
| [file other.py] |
| from typing import ClassVar, Dict |
| MY_DEFAULT: Dict[str, str] = {"key": "value"} |
| VAL_1: int = 10 |
| VAL_2: int = 20 |
| VAL_3: int = 30 |
| |
| class Parent: |
| config: Dict[str, str] = MY_DEFAULT |
| total: int = VAL_1 + VAL_2 + VAL_3 |
| TAG: ClassVar[str] = "parent" |
| |
| [file driver.py] |
| from native import test |
| test() |
| |
| [case testExtraOpsFunction_experimental_librt] |
| from librt.strings import BytesWriter |
| |
| def call_write(b: bytes) -> bytes: |
| w = BytesWriter() |
| w.write(b) # calls CPyBytesWriter_Write defined in byteswriter_extra_ops.c |
| return w.getvalue() |
| |
| [file other.py] |
| from native import call_write |
| |
| def test(b: bytes) -> bytes: |
| return call_write(b) |
| |
| [file driver.py] |
| from other import test |
| |
| input = b'x' * 512 |
| assert test(input) == input |
| |
| [case testLibrtDependencyInImportedFile_experimental_librt] |
| # Test that a compiled file (other.py) that does not depend on librt can import another compiled |
| # file (native.py) that does. |
| from librt.vecs import vec, append, pop |
| |
| def call_append(s: str) -> str: |
| v = vec[str]() |
| v = append(v, s) |
| _, rv = pop(v) |
| return rv |
| |
| [file other.py] |
| from native import call_append |
| |
| def test_append() -> None: |
| assert call_append("test123") == "test123" |
| |
| [file driver.py] |
| from other import test_append |
| |
| test_append() |
| |
| [case testIndirectLibrtDependency_experimental_librt] |
| # Test that a compiled file (other.py) that indirectly depends on librt can import another compiled |
| # file (native.py) that directly depends on it. |
| from librt.strings import BytesWriter, write_i16_le |
| from librt.vecs import vec, append, pop |
| from mypy_extensions import i64 |
| |
| glob_bw = BytesWriter() |
| glob_vec = vec[str](["one", "two", "three"]) |
| |
| def fn_returns_bw(b: bytes) -> BytesWriter: |
| w = BytesWriter() |
| w.write(b) |
| return w |
| |
| def fn_returns_vec(i: i64) -> vec[i64]: |
| v = vec[i64]() |
| v = append(v, i) |
| return v |
| |
| class ClassWithLibrtTypeMember: |
| def __init__(self, w: BytesWriter, v: vec[str]) -> None: |
| self.writer = w |
| self.vec = vec[vec[str]]([v]) |
| |
| def method_returns_bw(self) -> BytesWriter: |
| return self.writer |
| |
| def method_returns_vec(self) -> vec[vec[str]]: |
| return self.vec |
| |
| [file other.py] |
| import struct |
| |
| from native import ( |
| glob_bw, glob_vec, fn_returns_bw, fn_returns_vec, ClassWithLibrtTypeMember, write_i16_le, pop |
| ) |
| from mypy_extensions import i64 |
| |
| def test_global(b: bytes) -> None: |
| glob_bw.write(b) |
| assert glob_bw.getvalue() == b |
| |
| glob_vec[1] = "not two" |
| assert glob_vec[1] == "not two" |
| |
| def test_fn(b: bytes, i: i64) -> None: |
| assert fn_returns_bw(b).getvalue() == b |
| assert fn_returns_vec(i)[0] == i |
| |
| def test_class(b: bytes) -> None: |
| c = ClassWithLibrtTypeMember(glob_bw, glob_vec) |
| assert c.method_returns_bw().getvalue() == b |
| assert c.method_returns_vec()[0] == glob_vec |
| |
| def test_reexport() -> None: |
| write_i16_le(glob_bw, 42) |
| assert glob_bw.getvalue()[-2:] == struct.pack("<h", 42) |
| |
| glob_vec, s = pop(glob_vec) |
| assert len(glob_vec) == 2 |
| assert s == "three" |
| |
| [file driver.py] |
| from other import test_global, test_fn, test_class, test_reexport |
| |
| input = b'x' * 512 |
| test_global(input) |
| test_fn(input, 512) |
| test_class(input) |
| test_reexport() |
| |
| [case testLibrtInternalInImportedFile_librt_internal] |
| from librt.internal import ReadBuffer, WriteBuffer, write_str, read_str, cache_version |
| |
| def get_buffer_round_trip_result(s: str) -> str: |
| assert cache_version() == 0 |
| w = WriteBuffer() |
| write_str(w, s) |
| r = ReadBuffer(w.getvalue()) |
| return read_str(r) |
| |
| [file other.py] |
| from native import get_buffer_round_trip_result |
| |
| def test_buffer_round_trip() -> None: |
| assert get_buffer_round_trip_result("foo") == "foo" |
| |
| [file driver.py] |
| from other import test_buffer_round_trip |
| |
| test_buffer_round_trip() |
| |
| -- Regression tests for separate=True compilation. |
| |
| [case testSeparateCrossGroupEnumMethod] |
| -- Under separate=True, methods of non-extension classes (like Enum subclasses) |
| -- are compiled as direct C functions rather than vtable entries. |
| -- `is_method_final` must short-circuit to True for non-ext classes so the |
| -- cross-group call site emits a direct call rather than indexing into a |
| -- vtable that `compute_vtable` skipped. |
| from other_enum import Color |
| |
| def name_of_red() -> str: |
| return Color.RED.describe() |
| |
| def name_of_blue() -> str: |
| return Color.BLUE.describe() |
| |
| [file other_enum.py] |
| from enum import Enum |
| |
| class Color(Enum): |
| RED = 1 |
| BLUE = 2 |
| |
| def describe(self) -> str: |
| return "color:" + self.name |
| |
| [file driver.py] |
| from native import name_of_red, name_of_blue |
| assert name_of_red() == "color:RED" |
| assert name_of_blue() == "color:BLUE" |
| |
| [case testSeparateCrossGroupGenerator] |
| -- Under separate=True, mypyc-generated generator helper classes have their |
| -- FuncIR bodies in the defining group; consumers only see FuncDecl via |
| -- `method_decls`. `emit_method_call` must use `method_decl(name)` rather |
| -- than asserting `get_method(name).decl` is non-None. |
| from other_gen import count_up |
| |
| def sum_range(n: int) -> int: |
| total = 0 |
| for x in count_up(n): |
| total += x |
| return total |
| |
| [file other_gen.py] |
| from typing import Iterator |
| |
| def count_up(n: int) -> Iterator[int]: |
| i = 0 |
| while i < n: |
| yield i |
| i += 1 |
| |
| [file driver.py] |
| from native import sum_range |
| assert sum_range(0) == 0 |
| assert sum_range(1) == 0 |
| assert sum_range(5) == 10 |
| |
| [case testSeparateCrossGroupInheritedInit] |
| -- Under separate=True, a subclass whose __init__ is inherited from a |
| -- different group must call the base's CPyPy_ wrapper through the exports |
| -- table. This exercises `wrapper_function_call` in `generate_init_for_class` |
| -- / `emit_setup_or_dunder_new_call`, and requires the CPyPy_ wrapper decl |
| -- to be marked `needs_export=True` so it reaches the exports table. |
| from other_base import Base |
| |
| class Child(Base): |
| def label(self) -> str: |
| return "child(" + str(self.x) + ")" |
| |
| def make_child(n: int) -> str: |
| return Child(n).label() |
| |
| [file other_base.py] |
| class Base: |
| def __init__(self, x: int) -> None: |
| self.x = x |
| |
| [file driver.py] |
| from native import make_child |
| assert make_child(7) == "child(7)" |
| assert make_child(-1) == "child(-1)" |