Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,14 @@ env:
test_weakref
test_yield_from
ENV_POLLUTING_TESTS_COMMON: >-
test_threading
ENV_POLLUTING_TESTS_LINUX: >-
test.test_concurrent_futures.test_thread_pool
test.test_multiprocessing_fork.test_processes
test.test_multiprocessing_fork.test_threads
test.test_multiprocessing_forkserver.test_processes
test.test_multiprocessing_forkserver.test_threads
test.test_multiprocessing_spawn.test_processes
test.test_multiprocessing_spawn.test_threads
ENV_POLLUTING_TESTS_MACOS: >-
test.test_concurrent_futures.test_thread_pool
test.test_multiprocessing_forkserver.test_processes
test.test_multiprocessing_forkserver.test_threads
test.test_multiprocessing_spawn.test_processes
Expand Down Expand Up @@ -297,21 +294,21 @@ jobs:
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
run: target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed --timeout 600 -v ${{ env.PLATFORM_INDEPENDENT_TESTS }}
timeout-minutes: 35
timeout-minutes: 45

- if: runner.os == 'Linux'
name: run cpython platform-dependent tests (Linux)
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
run: target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed --timeout 600 -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
timeout-minutes: 35
timeout-minutes: 45

- if: runner.os == 'macOS'
name: run cpython platform-dependent tests (MacOS)
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
run: target/release/rustpython -m test -j 1 --slowest --fail-env-changed --timeout 600 -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
timeout-minutes: 35
timeout-minutes: 45

- if: runner.os == 'Windows'
name: run cpython platform-dependent tests (windows partial - fixme)
Expand Down
149 changes: 142 additions & 7 deletions Lib/_dummy_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,43 @@
import _dummy_thread as _thread

"""

# Exports only things specified by thread documentation;
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
'interrupt_main', 'LockType', 'RLock',
'_count']
__all__ = [
"error",
"start_new_thread",
"exit",
"get_ident",
"allocate_lock",
"interrupt_main",
"LockType",
"RLock",
"_count",
"start_joinable_thread",
"daemon_threads_allowed",
"_shutdown",
"_make_thread_handle",
"_ThreadHandle",
"_get_main_thread_ident",
"_is_main_interpreter",
"_local",
]

# A dummy value
TIMEOUT_MAX = 2**31

# Main thread ident for dummy implementation
_MAIN_THREAD_IDENT = -1

# NOTE: this module can be imported early in the extension building process,
# and so top level imports of other modules should be avoided. Instead, all
# imports are done when needed on a function-by-function basis. Since threads
# are disabled, the import lock should not be an issue anyway (??).

error = RuntimeError


def start_new_thread(function, args, kwargs={}):
"""Dummy implementation of _thread.start_new_thread().

Expand All @@ -52,44 +73,98 @@ def start_new_thread(function, args, kwargs={}):
pass
except:
import traceback

traceback.print_exc()
_main = True
global _interrupt
if _interrupt:
_interrupt = False
raise KeyboardInterrupt


def start_joinable_thread(function, handle=None, daemon=True):
"""Dummy implementation of _thread.start_joinable_thread().

In dummy thread, we just run the function synchronously.
"""
if handle is None:
handle = _ThreadHandle()
try:
function()
except SystemExit:
pass
except:
import traceback

traceback.print_exc()
handle._set_done()
return handle


def daemon_threads_allowed():
"""Dummy implementation of _thread.daemon_threads_allowed()."""
return True


def _shutdown():
"""Dummy implementation of _thread._shutdown()."""
pass


def _make_thread_handle(ident):
"""Dummy implementation of _thread._make_thread_handle()."""
handle = _ThreadHandle()
handle._ident = ident
return handle


def _get_main_thread_ident():
"""Dummy implementation of _thread._get_main_thread_ident()."""
return _MAIN_THREAD_IDENT


def _is_main_interpreter():
"""Dummy implementation of _thread._is_main_interpreter()."""
return True


def exit():
"""Dummy implementation of _thread.exit()."""
raise SystemExit


def get_ident():
"""Dummy implementation of _thread.get_ident().

Since this module should only be used when _threadmodule is not
available, it is safe to assume that the current process is the
only thread. Thus a constant can be safely returned.
"""
return -1
return _MAIN_THREAD_IDENT


def allocate_lock():
"""Dummy implementation of _thread.allocate_lock()."""
return LockType()


def stack_size(size=None):
"""Dummy implementation of _thread.stack_size()."""
if size is not None:
raise error("setting thread stack size not supported")
return 0


def _set_sentinel():
"""Dummy implementation of _thread._set_sentinel()."""
return LockType()


def _count():
"""Dummy implementation of _thread._count()."""
return 0


class LockType(object):
"""Class implementing dummy implementation of _thread.LockType.

Expand Down Expand Up @@ -125,6 +200,7 @@ def acquire(self, waitflag=None, timeout=-1):
else:
if timeout > 0:
import time

time.sleep(timeout)
return False

Expand Down Expand Up @@ -153,14 +229,41 @@ def __repr__(self):
"locked" if self.locked_status else "unlocked",
self.__class__.__module__,
self.__class__.__qualname__,
hex(id(self))
hex(id(self)),
)


class _ThreadHandle:
"""Dummy implementation of _thread._ThreadHandle."""

def __init__(self):
self._ident = _MAIN_THREAD_IDENT
self._done = False

@property
def ident(self):
return self._ident

def _set_done(self):
self._done = True

def is_done(self):
return self._done

def join(self, timeout=None):
# In dummy thread, thread is always done
return

def __repr__(self):
return f"<_ThreadHandle ident={self._ident}>"


# Used to signal that interrupt_main was called in a "thread"
_interrupt = False
# True when not executing in a "thread"
_main = True


def interrupt_main():
"""Set _interrupt flag to True to have start_new_thread raise
KeyboardInterrupt upon exiting."""
Expand All @@ -170,6 +273,7 @@ def interrupt_main():
global _interrupt
_interrupt = True


class RLock:
def __init__(self):
self.locked_count = 0
Expand All @@ -190,7 +294,7 @@ def release(self):
return True

def locked(self):
return self.locked_status != 0
return self.locked_count != 0

def __repr__(self):
return "<%s %s.%s object owner=%s count=%s at %s>" % (
Expand All @@ -199,5 +303,36 @@ def __repr__(self):
self.__class__.__qualname__,
get_ident() if self.locked_count else 0,
self.locked_count,
hex(id(self))
hex(id(self)),
)


class _local:
"""Dummy implementation of _thread._local (thread-local storage)."""

def __init__(self):
object.__setattr__(self, "_local__impl", {})

def __getattribute__(self, name):
if name.startswith("_local__"):
return object.__getattribute__(self, name)
impl = object.__getattribute__(self, "_local__impl")
try:
return impl[name]
except KeyError:
raise AttributeError(name)

def __setattr__(self, name, value):
if name.startswith("_local__"):
return object.__setattr__(self, name, value)
impl = object.__getattribute__(self, "_local__impl")
impl[name] = value

def __delattr__(self, name):
if name.startswith("_local__"):
return object.__delattr__(self, name)
impl = object.__getattribute__(self, "_local__impl")
try:
del impl[name]
except KeyError:
raise AttributeError(name)
38 changes: 21 additions & 17 deletions Lib/dummy_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
regardless of whether ``_thread`` was available which is not desired.

"""

from sys import modules as sys_modules

import _dummy_thread
Expand All @@ -19,59 +20,62 @@
# Could have checked if ``_thread`` was not in sys.modules and gone
# a different route, but decided to mirror technique used with
# ``threading`` below.
if '_thread' in sys_modules:
held_thread = sys_modules['_thread']
if "_thread" in sys_modules:
held_thread = sys_modules["_thread"]
holding_thread = True
# Must have some module named ``_thread`` that implements its API
# in order to initially import ``threading``.
sys_modules['_thread'] = sys_modules['_dummy_thread']
sys_modules["_thread"] = sys_modules["_dummy_thread"]

if 'threading' in sys_modules:
if "threading" in sys_modules:
# If ``threading`` is already imported, might as well prevent
# trying to import it more than needed by saving it if it is
# already imported before deleting it.
held_threading = sys_modules['threading']
held_threading = sys_modules["threading"]
holding_threading = True
del sys_modules['threading']
del sys_modules["threading"]

if '_threading_local' in sys_modules:
if "_threading_local" in sys_modules:
# If ``_threading_local`` is already imported, might as well prevent
# trying to import it more than needed by saving it if it is
# already imported before deleting it.
held__threading_local = sys_modules['_threading_local']
held__threading_local = sys_modules["_threading_local"]
holding__threading_local = True
del sys_modules['_threading_local']
del sys_modules["_threading_local"]

import threading

# Need a copy of the code kept somewhere...
sys_modules['_dummy_threading'] = sys_modules['threading']
del sys_modules['threading']
sys_modules['_dummy__threading_local'] = sys_modules['_threading_local']
del sys_modules['_threading_local']
sys_modules["_dummy_threading"] = sys_modules["threading"]
del sys_modules["threading"]
# _threading_local may not be imported if _thread._local is available
if "_threading_local" in sys_modules:
sys_modules["_dummy__threading_local"] = sys_modules["_threading_local"]
del sys_modules["_threading_local"]
from _dummy_threading import *
from _dummy_threading import __all__

finally:
# Put back ``threading`` if we overwrote earlier

if holding_threading:
sys_modules['threading'] = held_threading
sys_modules["threading"] = held_threading
del held_threading
del holding_threading

# Put back ``_threading_local`` if we overwrote earlier

if holding__threading_local:
sys_modules['_threading_local'] = held__threading_local
sys_modules["_threading_local"] = held__threading_local
del held__threading_local
del holding__threading_local

# Put back ``thread`` if we overwrote, else del the entry we made
if holding_thread:
sys_modules['_thread'] = held_thread
sys_modules["_thread"] = held_thread
del held_thread
else:
del sys_modules['_thread']
del sys_modules["_thread"]
del holding_thread

del _dummy_thread
Expand Down
Loading
Loading