2022-07-26 18:28:03 +00:00
|
|
|
import importlib
|
|
|
|
import platform
|
2023-09-14 12:40:43 +00:00
|
|
|
import sys
|
2022-07-26 18:28:03 +00:00
|
|
|
|
2021-04-01 23:03:49 +00:00
|
|
|
import pyo3_pytests.misc
|
2022-07-26 18:28:03 +00:00
|
|
|
import pytest
|
2021-04-01 23:03:49 +00:00
|
|
|
|
2024-05-25 22:41:26 +00:00
|
|
|
if sys.version_info >= (3, 13):
|
|
|
|
subinterpreters = pytest.importorskip("subinterpreters")
|
|
|
|
else:
|
|
|
|
subinterpreters = pytest.importorskip("_xxsubinterpreters")
|
|
|
|
|
2021-04-01 23:03:49 +00:00
|
|
|
|
|
|
|
def test_issue_219():
|
|
|
|
# Should not deadlock
|
|
|
|
pyo3_pytests.misc.issue_219()
|
2022-07-26 18:28:03 +00:00
|
|
|
|
|
|
|
|
2023-09-14 12:40:43 +00:00
|
|
|
@pytest.mark.xfail(
|
|
|
|
platform.python_implementation() == "CPython" and sys.version_info < (3, 9),
|
|
|
|
reason="Cannot identify subinterpreters on Python older than 3.9",
|
|
|
|
)
|
2023-09-11 13:20:39 +00:00
|
|
|
def test_multiple_imports_same_interpreter_ok():
|
|
|
|
spec = importlib.util.find_spec("pyo3_pytests.pyo3_pytests")
|
|
|
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
assert dir(module) == dir(pyo3_pytests.pyo3_pytests)
|
|
|
|
|
|
|
|
|
2023-09-14 12:40:43 +00:00
|
|
|
@pytest.mark.xfail(
|
|
|
|
platform.python_implementation() == "CPython" and sys.version_info < (3, 9),
|
|
|
|
reason="Cannot identify subinterpreters on Python older than 3.9",
|
|
|
|
)
|
2022-07-26 18:28:03 +00:00
|
|
|
@pytest.mark.skipif(
|
2024-03-25 18:54:52 +00:00
|
|
|
platform.python_implementation() in ("PyPy", "GraalVM"),
|
|
|
|
reason="PyPy and GraalPy do not support subinterpreters",
|
2022-07-26 18:28:03 +00:00
|
|
|
)
|
2023-09-11 13:20:39 +00:00
|
|
|
def test_import_in_subinterpreter_forbidden():
|
2023-09-29 12:08:47 +00:00
|
|
|
if sys.version_info < (3, 12):
|
|
|
|
expected_error = "PyO3 modules do not yet support subinterpreters, see https://github.com/PyO3/pyo3/issues/576"
|
|
|
|
else:
|
|
|
|
expected_error = "module pyo3_pytests.pyo3_pytests does not support loading in subinterpreters"
|
|
|
|
|
2024-05-25 22:41:26 +00:00
|
|
|
sub_interpreter = subinterpreters.create()
|
2022-07-26 18:28:03 +00:00
|
|
|
with pytest.raises(
|
2024-05-25 22:41:26 +00:00
|
|
|
subinterpreters.RunFailedError,
|
2023-09-29 12:08:47 +00:00
|
|
|
match=expected_error,
|
2022-07-26 18:28:03 +00:00
|
|
|
):
|
2024-05-25 22:41:26 +00:00
|
|
|
subinterpreters.run_string(sub_interpreter, "import pyo3_pytests.pyo3_pytests")
|
2023-09-11 13:20:39 +00:00
|
|
|
|
2024-05-25 22:41:26 +00:00
|
|
|
subinterpreters.destroy(sub_interpreter)
|
2023-12-18 08:57:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_type_full_name_includes_module():
|
|
|
|
numpy = pytest.importorskip("numpy")
|
|
|
|
|
2024-06-17 00:05:55 +00:00
|
|
|
# For numpy 1.x and 2.x
|
|
|
|
assert pyo3_pytests.misc.get_type_full_name(numpy.bool_(True)) in [
|
|
|
|
"numpy.bool",
|
|
|
|
"numpy.bool_",
|
|
|
|
]
|
2023-12-17 13:45:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_accepts_numpy_bool():
|
|
|
|
# binary numpy wheel not available on all platforms
|
|
|
|
numpy = pytest.importorskip("numpy")
|
|
|
|
|
|
|
|
assert pyo3_pytests.misc.accepts_bool(True) is True
|
|
|
|
assert pyo3_pytests.misc.accepts_bool(False) is False
|
|
|
|
assert pyo3_pytests.misc.accepts_bool(numpy.bool_(True)) is True
|
|
|
|
assert pyo3_pytests.misc.accepts_bool(numpy.bool_(False)) is False
|
2023-12-19 16:16:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ArbitraryClass:
|
|
|
|
worker_id: int
|
|
|
|
iteration: int
|
|
|
|
|
|
|
|
def __init__(self, worker_id: int, iteration: int):
|
|
|
|
self.worker_id = worker_id
|
|
|
|
self.iteration = iteration
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return f"ArbitraryClass({self.worker_id}, {self.iteration})"
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
print("del", self.worker_id, self.iteration)
|
|
|
|
|
|
|
|
|
|
|
|
def test_gevent():
|
2024-02-13 00:14:55 +00:00
|
|
|
gevent = pytest.importorskip("gevent")
|
|
|
|
|
2023-12-19 16:16:13 +00:00
|
|
|
def worker(worker_id: int) -> None:
|
|
|
|
for iteration in range(2):
|
|
|
|
d = {"key": ArbitraryClass(worker_id, iteration)}
|
|
|
|
|
|
|
|
def arbitrary_python_code():
|
|
|
|
# remove the dictionary entry so that the class value can be
|
|
|
|
# garbage collected
|
|
|
|
del d["key"]
|
|
|
|
print("gevent sleep", worker_id, iteration)
|
|
|
|
gevent.sleep(0)
|
|
|
|
print("after gevent sleep", worker_id, iteration)
|
|
|
|
|
|
|
|
print("start", worker_id, iteration)
|
|
|
|
pyo3_pytests.misc.get_item_and_run_callback(d, arbitrary_python_code)
|
|
|
|
print("end", worker_id, iteration)
|
|
|
|
|
|
|
|
workers = [gevent.spawn(worker, i) for i in range(2)]
|
|
|
|
gevent.joinall(workers)
|