summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/greenlet/tests
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/greenlet/tests')
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__init__.py237
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/__init__.cpython-311.pycbin9744 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pycbin2405 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pycbin1721 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pycbin3881 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pycbin1432 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pycbin1898 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pycbin2906 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pycbin1901 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/leakcheck.cpython-311.pycbin12777 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-311.pycbin18611 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-311.pycbin4567 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-311.pycbin8576 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_gc.cpython-311.pycbin5518 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator.cpython-311.pycbin3540 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-311.pycbin9394 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-311.pycbin86450 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pycbin7015 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-311.pycbin21765 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-311.pycbin1514 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_throw.cpython-311.pycbin8934 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-311.pycbin15840 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_version.cpython-311.pycbin2927 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-311.pycbin3080 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.c231
-rwxr-xr-xvenv/lib/python3.11/site-packages/greenlet/tests/_test_extension.cpython-311-x86_64-linux-gnu.sobin36624 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpp226
-rwxr-xr-xvenv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpython-311-x86_64-linux-gnu.sobin57288 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_clearing_run_switches.py47
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_cpp_exception.py33
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_initialstub_already_started.py78
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_slp_switch.py29
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets.py44
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets2.py55
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_two_greenlets.py41
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/leakcheck.py319
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_contextvars.py310
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_cpp.py73
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_extension_interface.py115
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_gc.py86
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_generator.py59
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_generator_nested.py168
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet.py1311
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet_trash.py178
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_leaks.py443
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_stack_saved.py19
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_throw.py128
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_tracing.py291
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_version.py41
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/tests/test_weakref.py35
50 files changed, 0 insertions, 4597 deletions
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__init__.py b/venv/lib/python3.11/site-packages/greenlet/tests/__init__.py
deleted file mode 100644
index e249e35..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__init__.py
+++ /dev/null
@@ -1,237 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Tests for greenlet.
-
-"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import sys
-import unittest
-
-from gc import collect
-from gc import get_objects
-from threading import active_count as active_thread_count
-from time import sleep
-from time import time
-
-import psutil
-
-from greenlet import greenlet as RawGreenlet
-from greenlet import getcurrent
-
-from greenlet._greenlet import get_pending_cleanup_count
-from greenlet._greenlet import get_total_main_greenlets
-
-from . import leakcheck
-
-PY312 = sys.version_info[:2] >= (3, 12)
-WIN = sys.platform.startswith("win")
-
-class TestCaseMetaClass(type):
- # wrap each test method with
- # a) leak checks
- def __new__(cls, classname, bases, classDict):
- # pylint and pep8 fight over what this should be called (mcs or cls).
- # pylint gets it right, but we can't scope disable pep8, so we go with
- # its convention.
- # pylint: disable=bad-mcs-classmethod-argument
- check_totalrefcount = True
-
- # Python 3: must copy, we mutate the classDict. Interestingly enough,
- # it doesn't actually error out, but under 3.6 we wind up wrapping
- # and re-wrapping the same items over and over and over.
- for key, value in list(classDict.items()):
- if key.startswith('test') and callable(value):
- classDict.pop(key)
- if check_totalrefcount:
- value = leakcheck.wrap_refcount(value)
- classDict[key] = value
- return type.__new__(cls, classname, bases, classDict)
-
-
-class TestCase(TestCaseMetaClass(
- "NewBase",
- (unittest.TestCase,),
- {})):
-
- cleanup_attempt_sleep_duration = 0.001
- cleanup_max_sleep_seconds = 1
-
- def wait_for_pending_cleanups(self,
- initial_active_threads=None,
- initial_main_greenlets=None):
- initial_active_threads = initial_active_threads or self.threads_before_test
- initial_main_greenlets = initial_main_greenlets or self.main_greenlets_before_test
- sleep_time = self.cleanup_attempt_sleep_duration
- # NOTE: This is racy! A Python-level thread object may be dead
- # and gone, but the C thread may not yet have fired its
- # destructors and added to the queue. There's no particular
- # way to know that's about to happen. We try to watch the
- # Python threads to make sure they, at least, have gone away.
- # Counting the main greenlets, which we can easily do deterministically,
- # also helps.
-
- # Always sleep at least once to let other threads run
- sleep(sleep_time)
- quit_after = time() + self.cleanup_max_sleep_seconds
- # TODO: We could add an API that calls us back when a particular main greenlet is deleted?
- # It would have to drop the GIL
- while (
- get_pending_cleanup_count()
- or active_thread_count() > initial_active_threads
- or (not self.expect_greenlet_leak
- and get_total_main_greenlets() > initial_main_greenlets)):
- sleep(sleep_time)
- if time() > quit_after:
- print("Time limit exceeded.")
- print("Threads: Waiting for only", initial_active_threads,
- "-->", active_thread_count())
- print("MGlets : Waiting for only", initial_main_greenlets,
- "-->", get_total_main_greenlets())
- break
- collect()
-
- def count_objects(self, kind=list, exact_kind=True):
- # pylint:disable=unidiomatic-typecheck
- # Collect the garbage.
- for _ in range(3):
- collect()
- if exact_kind:
- return sum(
- 1
- for x in get_objects()
- if type(x) is kind
- )
- # instances
- return sum(
- 1
- for x in get_objects()
- if isinstance(x, kind)
- )
-
- greenlets_before_test = 0
- threads_before_test = 0
- main_greenlets_before_test = 0
- expect_greenlet_leak = False
-
- def count_greenlets(self):
- """
- Find all the greenlets and subclasses tracked by the GC.
- """
- return self.count_objects(RawGreenlet, False)
-
- def setUp(self):
- # Ensure the main greenlet exists, otherwise the first test
- # gets a false positive leak
- super().setUp()
- getcurrent()
- self.threads_before_test = active_thread_count()
- self.main_greenlets_before_test = get_total_main_greenlets()
- self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test)
- self.greenlets_before_test = self.count_greenlets()
-
- def tearDown(self):
- if getattr(self, 'skipTearDown', False):
- return
-
- self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test)
- super().tearDown()
-
- def get_expected_returncodes_for_aborted_process(self):
- import signal
- # The child should be aborted in an unusual way. On POSIX
- # platforms, this is done with abort() and signal.SIGABRT,
- # which is reflected in a negative return value; however, on
- # Windows, even though we observe the child print "Fatal
- # Python error: Aborted" and in older versions of the C
- # runtime "This application has requested the Runtime to
- # terminate it in an unusual way," it always has an exit code
- # of 3. This is interesting because 3 is the error code for
- # ERROR_PATH_NOT_FOUND; BUT: the C runtime abort() function
- # also uses this code.
- #
- # If we link to the static C library on Windows, the error
- # code changes to '0xc0000409' (hex(3221226505)), which
- # apparently is STATUS_STACK_BUFFER_OVERRUN; but "What this
- # means is that nowadays when you get a
- # STATUS_STACK_BUFFER_OVERRUN, it doesn’t actually mean that
- # there is a stack buffer overrun. It just means that the
- # application decided to terminate itself with great haste."
- #
- #
- # On windows, we've also seen '0xc0000005' (hex(3221225477)).
- # That's "Access Violation"
- #
- # See
- # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623
- # and
- # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN
- # and
- # https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655
- expected_exit = (
- -signal.SIGABRT,
- # But beginning on Python 3.11, the faulthandler
- # that prints the C backtraces sometimes segfaults after
- # reporting the exception but before printing the stack.
- # This has only been seen on linux/gcc.
- -signal.SIGSEGV,
- ) if not WIN else (
- 3,
- 0xc0000409,
- 0xc0000005,
- )
- return expected_exit
-
- def get_process_uss(self):
- """
- Return the current process's USS in bytes.
-
- uss is available on Linux, macOS, Windows. Also known as
- "Unique Set Size", this is the memory which is unique to a
- process and which would be freed if the process was terminated
- right now.
-
- If this is not supported by ``psutil``, this raises the
- :exc:`unittest.SkipTest` exception.
- """
- try:
- return psutil.Process().memory_full_info().uss
- except AttributeError as e:
- raise unittest.SkipTest("uss not supported") from e
-
- def run_script(self, script_name, show_output=True):
- import subprocess
- import os
- script = os.path.join(
- os.path.dirname(__file__),
- script_name,
- )
-
- try:
- return subprocess.check_output([sys.executable, script],
- encoding='utf-8',
- stderr=subprocess.STDOUT)
- except subprocess.CalledProcessError as ex:
- if show_output:
- print('-----')
- print('Failed to run script', script)
- print('~~~~~')
- print(ex.output)
- print('------')
- raise
-
-
- def assertScriptRaises(self, script_name, exitcodes=None):
- import subprocess
- with self.assertRaises(subprocess.CalledProcessError) as exc:
- output = self.run_script(script_name, show_output=False)
- __traceback_info__ = output
- # We're going to fail the assertion if we get here, at least
- # preserve the output in the traceback.
-
- if exitcodes is None:
- exitcodes = self.get_expected_returncodes_for_aborted_process()
- self.assertIn(exc.exception.returncode, exitcodes)
- return exc.exception
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index 245565c..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/__init__.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pyc
deleted file mode 100644
index f8ca571..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pyc
deleted file mode 100644
index 1293a2e..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pyc
deleted file mode 100644
index 12af9c5..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pyc
deleted file mode 100644
index 41fa814..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pyc
deleted file mode 100644
index 73d0e65..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pyc
deleted file mode 100644
index dcdbc02..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pyc
deleted file mode 100644
index 97be12a..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/leakcheck.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/leakcheck.cpython-311.pyc
deleted file mode 100644
index 7a2616e..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/leakcheck.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-311.pyc
deleted file mode 100644
index afb0e23..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-311.pyc
deleted file mode 100644
index 3f24186..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-311.pyc
deleted file mode 100644
index 987e490..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_gc.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_gc.cpython-311.pyc
deleted file mode 100644
index c4edf24..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_gc.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator.cpython-311.pyc
deleted file mode 100644
index 936decb..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-311.pyc
deleted file mode 100644
index fa8ca9c..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-311.pyc
deleted file mode 100644
index 6f4f510..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pyc
deleted file mode 100644
index e40225d..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-311.pyc
deleted file mode 100644
index 88e931a..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-311.pyc
deleted file mode 100644
index b3f464d..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_throw.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_throw.cpython-311.pyc
deleted file mode 100644
index 2427360..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_throw.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-311.pyc
deleted file mode 100644
index a488241..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_version.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_version.cpython-311.pyc
deleted file mode 100644
index 16d09c1..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_version.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-311.pyc b/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-311.pyc
deleted file mode 100644
index ba0b403..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.c b/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.c
deleted file mode 100644
index 05e81c0..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/* This is a set of functions used by test_extension_interface.py to test the
- * Greenlet C API.
- */
-
-#include "../greenlet.h"
-
-#ifndef Py_RETURN_NONE
-# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
-#endif
-
-#define TEST_MODULE_NAME "_test_extension"
-
-static PyObject*
-test_switch(PyObject* self, PyObject* greenlet)
-{
- PyObject* result = NULL;
-
- if (greenlet == NULL || !PyGreenlet_Check(greenlet)) {
- PyErr_BadArgument();
- return NULL;
- }
-
- result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL);
- if (result == NULL) {
- if (!PyErr_Occurred()) {
- PyErr_SetString(PyExc_AssertionError,
- "greenlet.switch() failed for some reason.");
- }
- return NULL;
- }
- Py_INCREF(result);
- return result;
-}
-
-static PyObject*
-test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs)
-{
- PyGreenlet* g = NULL;
- PyObject* result = NULL;
-
- PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g);
-
- if (g == NULL || !PyGreenlet_Check(g)) {
- PyErr_BadArgument();
- return NULL;
- }
-
- result = PyGreenlet_Switch(g, NULL, kwargs);
- if (result == NULL) {
- if (!PyErr_Occurred()) {
- PyErr_SetString(PyExc_AssertionError,
- "greenlet.switch() failed for some reason.");
- }
- return NULL;
- }
- Py_XINCREF(result);
- return result;
-}
-
-static PyObject*
-test_getcurrent(PyObject* self)
-{
- PyGreenlet* g = PyGreenlet_GetCurrent();
- if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) {
- PyErr_SetString(PyExc_AssertionError,
- "getcurrent() returned an invalid greenlet");
- Py_XDECREF(g);
- return NULL;
- }
- Py_DECREF(g);
- Py_RETURN_NONE;
-}
-
-static PyObject*
-test_setparent(PyObject* self, PyObject* arg)
-{
- PyGreenlet* current;
- PyGreenlet* greenlet = NULL;
-
- if (arg == NULL || !PyGreenlet_Check(arg)) {
- PyErr_BadArgument();
- return NULL;
- }
- if ((current = PyGreenlet_GetCurrent()) == NULL) {
- return NULL;
- }
- greenlet = (PyGreenlet*)arg;
- if (PyGreenlet_SetParent(greenlet, current)) {
- Py_DECREF(current);
- return NULL;
- }
- Py_DECREF(current);
- if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) {
- return NULL;
- }
- Py_RETURN_NONE;
-}
-
-static PyObject*
-test_new_greenlet(PyObject* self, PyObject* callable)
-{
- PyObject* result = NULL;
- PyGreenlet* greenlet = PyGreenlet_New(callable, NULL);
-
- if (!greenlet) {
- return NULL;
- }
-
- result = PyGreenlet_Switch(greenlet, NULL, NULL);
- Py_CLEAR(greenlet);
- if (result == NULL) {
- return NULL;
- }
-
- Py_INCREF(result);
- return result;
-}
-
-static PyObject*
-test_raise_dead_greenlet(PyObject* self)
-{
- PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception.");
- return NULL;
-}
-
-static PyObject*
-test_raise_greenlet_error(PyObject* self)
-{
- PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception");
- return NULL;
-}
-
-static PyObject*
-test_throw(PyObject* self, PyGreenlet* g)
-{
- const char msg[] = "take that sucka!";
- PyObject* msg_obj = Py_BuildValue("s", msg);
- PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL);
- Py_DECREF(msg_obj);
- if (PyErr_Occurred()) {
- return NULL;
- }
- Py_RETURN_NONE;
-}
-
-static PyObject*
-test_throw_exact(PyObject* self, PyObject* args)
-{
- PyGreenlet* g = NULL;
- PyObject* typ = NULL;
- PyObject* val = NULL;
- PyObject* tb = NULL;
-
- if (!PyArg_ParseTuple(args, "OOOO:throw", &g, &typ, &val, &tb)) {
- return NULL;
- }
-
- PyGreenlet_Throw(g, typ, val, tb);
- if (PyErr_Occurred()) {
- return NULL;
- }
- Py_RETURN_NONE;
-}
-
-static PyMethodDef test_methods[] = {
- {"test_switch",
- (PyCFunction)test_switch,
- METH_O,
- "Switch to the provided greenlet sending provided arguments, and \n"
- "return the results."},
- {"test_switch_kwargs",
- (PyCFunction)test_switch_kwargs,
- METH_VARARGS | METH_KEYWORDS,
- "Switch to the provided greenlet sending the provided keyword args."},
- {"test_getcurrent",
- (PyCFunction)test_getcurrent,
- METH_NOARGS,
- "Test PyGreenlet_GetCurrent()"},
- {"test_setparent",
- (PyCFunction)test_setparent,
- METH_O,
- "Se the parent of the provided greenlet and switch to it."},
- {"test_new_greenlet",
- (PyCFunction)test_new_greenlet,
- METH_O,
- "Test PyGreenlet_New()"},
- {"test_raise_dead_greenlet",
- (PyCFunction)test_raise_dead_greenlet,
- METH_NOARGS,
- "Just raise greenlet.GreenletExit"},
- {"test_raise_greenlet_error",
- (PyCFunction)test_raise_greenlet_error,
- METH_NOARGS,
- "Just raise greenlet.error"},
- {"test_throw",
- (PyCFunction)test_throw,
- METH_O,
- "Throw a ValueError at the provided greenlet"},
- {"test_throw_exact",
- (PyCFunction)test_throw_exact,
- METH_VARARGS,
- "Throw exactly the arguments given at the provided greenlet"},
- {NULL, NULL, 0, NULL}
-};
-
-
-#define INITERROR return NULL
-
-static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
- TEST_MODULE_NAME,
- NULL,
- 0,
- test_methods,
- NULL,
- NULL,
- NULL,
- NULL};
-
-PyMODINIT_FUNC
-PyInit__test_extension(void)
-{
- PyObject* module = NULL;
- module = PyModule_Create(&moduledef);
-
- if (module == NULL) {
- return NULL;
- }
-
- PyGreenlet_Import();
- return module;
-}
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.cpython-311-x86_64-linux-gnu.so b/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.cpython-311-x86_64-linux-gnu.so
deleted file mode 100755
index 2c7fff0..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension.cpython-311-x86_64-linux-gnu.so
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpp b/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpp
deleted file mode 100644
index 5cbe6a7..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/* This is a set of functions used to test C++ exceptions are not
- * broken during greenlet switches
- */
-
-#include "../greenlet.h"
-#include "../greenlet_compiler_compat.hpp"
-#include <exception>
-#include <stdexcept>
-
-struct exception_t {
- int depth;
- exception_t(int depth) : depth(depth) {}
-};
-
-/* Functions are called via pointers to prevent inlining */
-static void (*p_test_exception_throw_nonstd)(int depth);
-static void (*p_test_exception_throw_std)();
-static PyObject* (*p_test_exception_switch_recurse)(int depth, int left);
-
-static void
-test_exception_throw_nonstd(int depth)
-{
- throw exception_t(depth);
-}
-
-static void
-test_exception_throw_std()
-{
- throw std::runtime_error("Thrown from an extension.");
-}
-
-static PyObject*
-test_exception_switch_recurse(int depth, int left)
-{
- if (left > 0) {
- return p_test_exception_switch_recurse(depth, left - 1);
- }
-
- PyObject* result = NULL;
- PyGreenlet* self = PyGreenlet_GetCurrent();
- if (self == NULL)
- return NULL;
-
- try {
- if (PyGreenlet_Switch(PyGreenlet_GET_PARENT(self), NULL, NULL) == NULL) {
- Py_DECREF(self);
- return NULL;
- }
- p_test_exception_throw_nonstd(depth);
- PyErr_SetString(PyExc_RuntimeError,
- "throwing C++ exception didn't work");
- }
- catch (const exception_t& e) {
- if (e.depth != depth)
- PyErr_SetString(PyExc_AssertionError, "depth mismatch");
- else
- result = PyLong_FromLong(depth);
- }
- catch (...) {
- PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception");
- }
-
- Py_DECREF(self);
- return result;
-}
-
-/* test_exception_switch(int depth)
- * - recurses depth times
- * - switches to parent inside try/catch block
- * - throws an exception that (expected to be caught in the same function)
- * - verifies depth matches (exceptions shouldn't be caught in other greenlets)
- */
-static PyObject*
-test_exception_switch(PyObject* UNUSED(self), PyObject* args)
-{
- int depth;
- if (!PyArg_ParseTuple(args, "i", &depth))
- return NULL;
- return p_test_exception_switch_recurse(depth, depth);
-}
-
-
-static PyObject*
-py_test_exception_throw_nonstd(PyObject* self, PyObject* args)
-{
- if (!PyArg_ParseTuple(args, ""))
- return NULL;
- p_test_exception_throw_nonstd(0);
- PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw");
- return NULL;
-}
-
-static PyObject*
-py_test_exception_throw_std(PyObject* self, PyObject* args)
-{
- if (!PyArg_ParseTuple(args, ""))
- return NULL;
- p_test_exception_throw_std();
- PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw");
- return NULL;
-}
-
-static PyObject*
-py_test_call(PyObject* self, PyObject* arg)
-{
- PyObject* noargs = PyTuple_New(0);
- PyObject* ret = PyObject_Call(arg, noargs, nullptr);
- Py_DECREF(noargs);
- return ret;
-}
-
-
-
-/* test_exception_switch_and_do_in_g2(g2func)
- * - creates new greenlet g2 to run g2func
- * - switches to g2 inside try/catch block
- * - verifies that no exception has been caught
- *
- * it is used together with test_exception_throw to verify that unhandled
- * exceptions thrown in one greenlet do not propagate to other greenlet nor
- * segfault the process.
- */
-static PyObject*
-test_exception_switch_and_do_in_g2(PyObject* self, PyObject* args)
-{
- PyObject* g2func = NULL;
- PyObject* result = NULL;
-
- if (!PyArg_ParseTuple(args, "O", &g2func))
- return NULL;
- PyGreenlet* g2 = PyGreenlet_New(g2func, NULL);
- if (!g2) {
- return NULL;
- }
-
- try {
- result = PyGreenlet_Switch(g2, NULL, NULL);
- if (!result) {
- return NULL;
- }
- }
- catch (const exception_t& e) {
- /* if we are here the memory can be already corrupted and the program
- * might crash before below py-level exception might become printed.
- * -> print something to stderr to make it clear that we had entered
- * this catch block.
- * See comments in inner_bootstrap()
- */
-#if defined(WIN32) || defined(_WIN32)
- fprintf(stderr, "C++ exception unexpectedly caught in g1\n");
- PyErr_SetString(PyExc_AssertionError, "C++ exception unexpectedly caught in g1");
- Py_XDECREF(result);
- return NULL;
-#else
- throw;
-#endif
- }
-
- Py_XDECREF(result);
- Py_RETURN_NONE;
-}
-
-static PyMethodDef test_methods[] = {
- {"test_exception_switch",
- (PyCFunction)&test_exception_switch,
- METH_VARARGS,
- "Switches to parent twice, to test exception handling and greenlet "
- "switching."},
- {"test_exception_switch_and_do_in_g2",
- (PyCFunction)&test_exception_switch_and_do_in_g2,
- METH_VARARGS,
- "Creates new greenlet g2 to run g2func and switches to it inside try/catch "
- "block. Used together with test_exception_throw to verify that unhandled "
- "C++ exceptions thrown in a greenlet doe not corrupt memory."},
- {"test_exception_throw_nonstd",
- (PyCFunction)&py_test_exception_throw_nonstd,
- METH_VARARGS,
- "Throws non-standard C++ exception. Calling this function directly should abort the process."
- },
- {"test_exception_throw_std",
- (PyCFunction)&py_test_exception_throw_std,
- METH_VARARGS,
- "Throws standard C++ exception. Calling this function directly should abort the process."
- },
- {"test_call",
- (PyCFunction)&py_test_call,
- METH_O,
- "Call the given callable. Unlike calling it directly, this creates a "
- "new C-level stack frame, which may be helpful in testing."
- },
- {NULL, NULL, 0, NULL}
-};
-
-
-static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
- "greenlet.tests._test_extension_cpp",
- NULL,
- 0,
- test_methods,
- NULL,
- NULL,
- NULL,
- NULL};
-
-PyMODINIT_FUNC
-PyInit__test_extension_cpp(void)
-{
- PyObject* module = NULL;
-
- module = PyModule_Create(&moduledef);
-
- if (module == NULL) {
- return NULL;
- }
-
- PyGreenlet_Import();
- if (_PyGreenlet_API == NULL) {
- return NULL;
- }
-
- p_test_exception_throw_nonstd = test_exception_throw_nonstd;
- p_test_exception_throw_std = test_exception_throw_std;
- p_test_exception_switch_recurse = test_exception_switch_recurse;
-
- return module;
-}
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpython-311-x86_64-linux-gnu.so b/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpython-311-x86_64-linux-gnu.so
deleted file mode 100755
index 714dfa8..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/_test_extension_cpp.cpython-311-x86_64-linux-gnu.so
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_clearing_run_switches.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_clearing_run_switches.py
deleted file mode 100644
index 6dd1492..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_clearing_run_switches.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-If we have a run callable passed to the constructor or set as an
-attribute, but we don't actually use that (because ``__getattribute__``
-or the like interferes), then when we clear callable before beginning
-to run, there's an opportunity for Python code to run.
-
-"""
-import greenlet
-
-g = None
-main = greenlet.getcurrent()
-
-results = []
-
-class RunCallable:
-
- def __del__(self):
- results.append(('RunCallable', '__del__'))
- main.switch('from RunCallable')
-
-
-class G(greenlet.greenlet):
-
- def __getattribute__(self, name):
- if name == 'run':
- results.append(('G.__getattribute__', 'run'))
- return run_func
- return object.__getattribute__(self, name)
-
-
-def run_func():
- results.append(('run_func', 'enter'))
-
-
-g = G(RunCallable())
-# Try to start G. It will get to the point where it deletes
-# its run callable C++ variable in inner_bootstrap. That triggers
-# the __del__ method, which switches back to main before g
-# actually even starts running.
-x = g.switch()
-results.append(('main: g.switch()', x))
-# In the C++ code, this results in g->g_switch() appearing to return, even though
-# it has yet to run.
-print('In main with', x, flush=True)
-g.switch()
-print('RESULTS', results)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_cpp_exception.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_cpp_exception.py
deleted file mode 100644
index fa4dc2e..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_cpp_exception.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Helper for testing a C++ exception throw aborts the process.
-
-Takes one argument, the name of the function in :mod:`_test_extension_cpp` to call.
-"""
-import sys
-import greenlet
-from greenlet.tests import _test_extension_cpp
-print('fail_cpp_exception is running')
-
-def run_unhandled_exception_in_greenlet_aborts():
- def _():
- _test_extension_cpp.test_exception_switch_and_do_in_g2(
- _test_extension_cpp.test_exception_throw_nonstd
- )
- g1 = greenlet.greenlet(_)
- g1.switch()
-
-
-func_name = sys.argv[1]
-try:
- func = getattr(_test_extension_cpp, func_name)
-except AttributeError:
- if func_name == run_unhandled_exception_in_greenlet_aborts.__name__:
- func = run_unhandled_exception_in_greenlet_aborts
- elif func_name == 'run_as_greenlet_target':
- g = greenlet.greenlet(_test_extension_cpp.test_exception_throw_std)
- func = g.switch
- else:
- raise
-print('raising', func, flush=True)
-func()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_initialstub_already_started.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_initialstub_already_started.py
deleted file mode 100644
index c1a44ef..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_initialstub_already_started.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""
-Testing initialstub throwing an already started exception.
-"""
-
-import greenlet
-
-a = None
-b = None
-c = None
-main = greenlet.getcurrent()
-
-# If we switch into a dead greenlet,
-# we go looking for its parents.
-# if a parent is not yet started, we start it.
-
-results = []
-
-def a_run(*args):
- #results.append('A')
- results.append(('Begin A', args))
-
-
-def c_run():
- results.append('Begin C')
- b.switch('From C')
- results.append('C done')
-
-class A(greenlet.greenlet): pass
-
-class B(greenlet.greenlet):
- doing_it = False
- def __getattribute__(self, name):
- if name == 'run' and not self.doing_it:
- assert greenlet.getcurrent() is c
- self.doing_it = True
- results.append('Switch to b from B.__getattribute__ in '
- + type(greenlet.getcurrent()).__name__)
- b.switch()
- results.append('B.__getattribute__ back from main in '
- + type(greenlet.getcurrent()).__name__)
- if name == 'run':
- name = '_B_run'
- return object.__getattribute__(self, name)
-
- def _B_run(self, *arg):
- results.append(('Begin B', arg))
- results.append('_B_run switching to main')
- main.switch('From B')
-
-class C(greenlet.greenlet):
- pass
-a = A(a_run)
-b = B(parent=a)
-c = C(c_run, b)
-
-# Start a child; while running, it will start B,
-# but starting B will ALSO start B.
-result = c.switch()
-results.append(('main from c', result))
-
-# Switch back to C, which was in the middle of switching
-# already. This will throw the ``GreenletStartedWhileInPython``
-# exception, which results in parent A getting started (B is finished)
-c.switch()
-
-results.append(('A dead?', a.dead, 'B dead?', b.dead, 'C dead?', c.dead))
-
-# A and B should both be dead now.
-assert a.dead
-assert b.dead
-assert not c.dead
-
-result = c.switch()
-results.append(('main from c.2', result))
-# Now C is dead
-assert c.dead
-
-print("RESULTS:", results)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_slp_switch.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_slp_switch.py
deleted file mode 100644
index 0990526..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_slp_switch.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-A test helper for seeing what happens when slp_switch()
-fails.
-"""
-# pragma: no cover
-
-import greenlet
-
-
-print('fail_slp_switch is running', flush=True)
-
-runs = []
-def func():
- runs.append(1)
- greenlet.getcurrent().parent.switch()
- runs.append(2)
- greenlet.getcurrent().parent.switch()
- runs.append(3)
-
-g = greenlet._greenlet.UnswitchableGreenlet(func)
-g.switch()
-assert runs == [1]
-g.switch()
-assert runs == [1, 2]
-g.force_slp_switch_error = True
-
-# This should crash.
-g.switch()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets.py
deleted file mode 100644
index e151b19..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-Uses a trace function to switch greenlets at unexpected times.
-
-In the trace function, we switch from the current greenlet to another
-greenlet, which switches
-"""
-import greenlet
-
-g1 = None
-g2 = None
-
-switch_to_g2 = False
-
-def tracefunc(*args):
- print('TRACE', *args)
- global switch_to_g2
- if switch_to_g2:
- switch_to_g2 = False
- g2.switch()
- print('\tLEAVE TRACE', *args)
-
-def g1_run():
- print('In g1_run')
- global switch_to_g2
- switch_to_g2 = True
- from_parent = greenlet.getcurrent().parent.switch()
- print('Return to g1_run')
- print('From parent', from_parent)
-
-def g2_run():
- #g1.switch()
- greenlet.getcurrent().parent.switch()
-
-greenlet.settrace(tracefunc)
-
-g1 = greenlet.greenlet(g1_run)
-g2 = greenlet.greenlet(g2_run)
-
-# This switch didn't actually finish!
-# And if it did, it would raise TypeError
-# because g1_run() doesn't take any arguments.
-g1.switch(1)
-print('Back in main')
-g1.switch(2)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets2.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets2.py
deleted file mode 100644
index 1f6b66b..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_three_greenlets2.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-Like fail_switch_three_greenlets, but the call into g1_run would actually be
-valid.
-"""
-import greenlet
-
-g1 = None
-g2 = None
-
-switch_to_g2 = True
-
-results = []
-
-def tracefunc(*args):
- results.append(('trace', args[0]))
- print('TRACE', *args)
- global switch_to_g2
- if switch_to_g2:
- switch_to_g2 = False
- g2.switch('g2 from tracefunc')
- print('\tLEAVE TRACE', *args)
-
-def g1_run(arg):
- results.append(('g1 arg', arg))
- print('In g1_run')
- from_parent = greenlet.getcurrent().parent.switch('from g1_run')
- results.append(('g1 from parent', from_parent))
- return 'g1 done'
-
-def g2_run(arg):
- #g1.switch()
- results.append(('g2 arg', arg))
- parent = greenlet.getcurrent().parent.switch('from g2_run')
- global switch_to_g2
- switch_to_g2 = False
- results.append(('g2 from parent', parent))
- return 'g2 done'
-
-
-greenlet.settrace(tracefunc)
-
-g1 = greenlet.greenlet(g1_run)
-g2 = greenlet.greenlet(g2_run)
-
-x = g1.switch('g1 from main')
-results.append(('main g1', x))
-print('Back in main', x)
-x = g1.switch('g2 from main')
-results.append(('main g2', x))
-print('back in amain again', x)
-x = g1.switch('g1 from main 2')
-results.append(('main g1.2', x))
-x = g2.switch()
-results.append(('main g2.2', x))
-print("RESULTS:", results)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_two_greenlets.py b/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_two_greenlets.py
deleted file mode 100644
index 3e52345..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/fail_switch_two_greenlets.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Uses a trace function to switch greenlets at unexpected times.
-
-In the trace function, we switch from the current greenlet to another
-greenlet, which switches
-"""
-import greenlet
-
-g1 = None
-g2 = None
-
-switch_to_g2 = False
-
-def tracefunc(*args):
- print('TRACE', *args)
- global switch_to_g2
- if switch_to_g2:
- switch_to_g2 = False
- g2.switch()
- print('\tLEAVE TRACE', *args)
-
-def g1_run():
- print('In g1_run')
- global switch_to_g2
- switch_to_g2 = True
- greenlet.getcurrent().parent.switch()
- print('Return to g1_run')
- print('Falling off end of g1_run')
-
-def g2_run():
- g1.switch()
- print('Falling off end of g2')
-
-greenlet.settrace(tracefunc)
-
-g1 = greenlet.greenlet(g1_run)
-g2 = greenlet.greenlet(g2_run)
-
-g1.switch()
-print('Falling off end of main')
-g2.switch()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/leakcheck.py b/venv/lib/python3.11/site-packages/greenlet/tests/leakcheck.py
deleted file mode 100644
index a5152fb..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/leakcheck.py
+++ /dev/null
@@ -1,319 +0,0 @@
-# Copyright (c) 2018 gevent community
-# Copyright (c) 2021 greenlet community
-#
-# This was originally part of gevent's test suite. The main author
-# (Jason Madden) vendored a copy of it into greenlet.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-from __future__ import print_function
-
-import os
-import sys
-import gc
-
-from functools import wraps
-import unittest
-
-
-import objgraph
-
-# graphviz 0.18 (Nov 7 2021), available only on Python 3.6 and newer,
-# has added type hints (sigh). It wants to use ``typing.Literal`` for
-# some stuff, but that's only available on Python 3.9+. If that's not
-# found, it creates a ``unittest.mock.MagicMock`` object and annotates
-# with that. These are GC'able objects, and doing almost *anything*
-# with them results in an explosion of objects. For example, trying to
-# compare them for equality creates new objects. This causes our
-# leakchecks to fail, with reports like:
-#
-# greenlet.tests.leakcheck.LeakCheckError: refcount increased by [337, 1333, 343, 430, 530, 643, 769]
-# _Call 1820 +546
-# dict 4094 +76
-# MagicProxy 585 +73
-# tuple 2693 +66
-# _CallList 24 +3
-# weakref 1441 +1
-# function 5996 +1
-# type 736 +1
-# cell 592 +1
-# MagicMock 8 +1
-#
-# To avoid this, we *could* filter this type of object out early. In
-# principle it could leak, but we don't use mocks in greenlet, so it
-# doesn't leak from us. However, a further issue is that ``MagicMock``
-# objects have subobjects that are also GC'able, like ``_Call``, and
-# those create new mocks of their own too. So we'd have to filter them
-# as well, and they're not public. That's OK, we can workaround the
-# problem by being very careful to never compare by equality or other
-# user-defined operators, only using object identity or other builtin
-# functions.
-
-RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS')
-RUNNING_ON_TRAVIS = os.environ.get('TRAVIS') or RUNNING_ON_GITHUB_ACTIONS
-RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR')
-RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR
-RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX')
-SKIP_LEAKCHECKS = RUNNING_ON_MANYLINUX or os.environ.get('GREENLET_SKIP_LEAKCHECKS')
-SKIP_FAILING_LEAKCHECKS = os.environ.get('GREENLET_SKIP_FAILING_LEAKCHECKS')
-ONLY_FAILING_LEAKCHECKS = os.environ.get('GREENLET_ONLY_FAILING_LEAKCHECKS')
-
-def ignores_leakcheck(func):
- """
- Ignore the given object during leakchecks.
-
- Can be applied to a method, in which case the method will run, but
- will not be subject to leak checks.
-
- If applied to a class, the entire class will be skipped during leakchecks. This
- is intended to be used for classes that are very slow and cause problems such as
- test timeouts; typically it will be used for classes that are subclasses of a base
- class and specify variants of behaviour (such as pool sizes).
- """
- func.ignore_leakcheck = True
- return func
-
-def fails_leakcheck(func):
- """
- Mark that the function is known to leak.
- """
- func.fails_leakcheck = True
- if SKIP_FAILING_LEAKCHECKS:
- func = unittest.skip("Skipping known failures")(func)
- return func
-
-class LeakCheckError(AssertionError):
- pass
-
-if hasattr(sys, 'getobjects'):
- # In a Python build with ``--with-trace-refs``, make objgraph
- # trace *all* the objects, not just those that are tracked by the
- # GC
- class _MockGC(object):
- def get_objects(self):
- return sys.getobjects(0) # pylint:disable=no-member
- def __getattr__(self, name):
- return getattr(gc, name)
- objgraph.gc = _MockGC()
- fails_strict_leakcheck = fails_leakcheck
-else:
- def fails_strict_leakcheck(func):
- """
- Decorator for a function that is known to fail when running
- strict (``sys.getobjects()``) leakchecks.
-
- This type of leakcheck finds all objects, even those, such as
- strings, which are not tracked by the garbage collector.
- """
- return func
-
-class ignores_types_in_strict_leakcheck(object):
- def __init__(self, types):
- self.types = types
- def __call__(self, func):
- func.leakcheck_ignore_types = self.types
- return func
-
-class _RefCountChecker(object):
-
- # Some builtin things that we ignore
- # XXX: Those things were ignored by gevent, but they're important here,
- # presumably.
- IGNORED_TYPES = () #(tuple, dict, types.FrameType, types.TracebackType)
-
- def __init__(self, testcase, function):
- self.testcase = testcase
- self.function = function
- self.deltas = []
- self.peak_stats = {}
- self.ignored_types = ()
-
- # The very first time we are called, we have already been
- # self.setUp() by the test runner, so we don't need to do it again.
- self.needs_setUp = False
-
- def _include_object_p(self, obj):
- # pylint:disable=too-many-return-statements
- #
- # See the comment block at the top. We must be careful to
- # avoid invoking user-defined operations.
- if obj is self:
- return False
- kind = type(obj)
- # ``self._include_object_p == obj`` returns NotImplemented
- # for non-function objects, which causes the interpreter
- # to try to reverse the order of arguments...which leads
- # to the explosion of mock objects. We don't want that, so we implement
- # the check manually.
- if kind == type(self._include_object_p):
- try:
- # pylint:disable=not-callable
- exact_method_equals = self._include_object_p.__eq__(obj)
- except AttributeError:
- # Python 2.7 methods may only have __cmp__, and that raises a
- # TypeError for non-method arguments
- # pylint:disable=no-member
- exact_method_equals = self._include_object_p.__cmp__(obj) == 0
-
- if exact_method_equals is not NotImplemented and exact_method_equals:
- return False
-
- # Similarly, we need to check identity in our __dict__ to avoid mock explosions.
- for x in self.__dict__.values():
- if obj is x:
- return False
-
-
- if kind in self.ignored_types or kind in self.IGNORED_TYPES:
- return False
-
- return True
-
- def _growth(self):
- return objgraph.growth(limit=None, peak_stats=self.peak_stats,
- filter=self._include_object_p)
-
- def _report_diff(self, growth):
- if not growth:
- return "<Unable to calculate growth>"
-
- lines = []
- width = max(len(name) for name, _, _ in growth)
- for name, count, delta in growth:
- lines.append('%-*s%9d %+9d' % (width, name, count, delta))
-
- diff = '\n'.join(lines)
- return diff
-
-
- def _run_test(self, args, kwargs):
- gc_enabled = gc.isenabled()
- gc.disable()
-
- if self.needs_setUp:
- self.testcase.setUp()
- self.testcase.skipTearDown = False
- try:
- self.function(self.testcase, *args, **kwargs)
- finally:
- self.testcase.tearDown()
- self.testcase.doCleanups()
- self.testcase.skipTearDown = True
- self.needs_setUp = True
- if gc_enabled:
- gc.enable()
-
- def _growth_after(self):
- # Grab post snapshot
- # pylint:disable=no-member
- if 'urlparse' in sys.modules:
- sys.modules['urlparse'].clear_cache()
- if 'urllib.parse' in sys.modules:
- sys.modules['urllib.parse'].clear_cache()
-
- return self._growth()
-
- def _check_deltas(self, growth):
- # Return false when we have decided there is no leak,
- # true if we should keep looping, raises an assertion
- # if we have decided there is a leak.
-
- deltas = self.deltas
- if not deltas:
- # We haven't run yet, no data, keep looping
- return True
-
- if gc.garbage:
- raise LeakCheckError("Generated uncollectable garbage %r" % (gc.garbage,))
-
-
- # the following configurations are classified as "no leak"
- # [0, 0]
- # [x, 0, 0]
- # [... a, b, c, d] where a+b+c+d = 0
- #
- # the following configurations are classified as "leak"
- # [... z, z, z] where z > 0
-
- if deltas[-2:] == [0, 0] and len(deltas) in (2, 3):
- return False
-
- if deltas[-3:] == [0, 0, 0]:
- return False
-
- if len(deltas) >= 4 and sum(deltas[-4:]) == 0:
- return False
-
- if len(deltas) >= 3 and deltas[-1] > 0 and deltas[-1] == deltas[-2] and deltas[-2] == deltas[-3]:
- diff = self._report_diff(growth)
- raise LeakCheckError('refcount increased by %r\n%s' % (deltas, diff))
-
- # OK, we don't know for sure yet. Let's search for more
- if sum(deltas[-3:]) <= 0 or sum(deltas[-4:]) <= 0 or deltas[-4:].count(0) >= 2:
- # this is suspicious, so give a few more runs
- limit = 11
- else:
- limit = 7
- if len(deltas) >= limit:
- raise LeakCheckError('refcount increased by %r\n%s'
- % (deltas,
- self._report_diff(growth)))
-
- # We couldn't decide yet, keep going
- return True
-
- def __call__(self, args, kwargs):
- for _ in range(3):
- gc.collect()
-
- expect_failure = getattr(self.function, 'fails_leakcheck', False)
- if expect_failure:
- self.testcase.expect_greenlet_leak = True
- self.ignored_types = getattr(self.function, "leakcheck_ignore_types", ())
-
- # Capture state before; the incremental will be
- # updated by each call to _growth_after
- growth = self._growth()
-
- try:
- while self._check_deltas(growth):
- self._run_test(args, kwargs)
-
- growth = self._growth_after()
-
- self.deltas.append(sum((stat[2] for stat in growth)))
- except LeakCheckError:
- if not expect_failure:
- raise
- else:
- if expect_failure:
- raise LeakCheckError("Expected %s to leak but it did not." % (self.function,))
-
-def wrap_refcount(method):
- if getattr(method, 'ignore_leakcheck', False) or SKIP_LEAKCHECKS:
- return method
-
- @wraps(method)
- def wrapper(self, *args, **kwargs): # pylint:disable=too-many-branches
- if getattr(self, 'ignore_leakcheck', False):
- raise unittest.SkipTest("This class ignored during leakchecks")
- if ONLY_FAILING_LEAKCHECKS and not getattr(method, 'fails_leakcheck', False):
- raise unittest.SkipTest("Only running tests that fail leakchecks.")
- return _RefCountChecker(self, method)(args, kwargs)
-
- return wrapper
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_contextvars.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_contextvars.py
deleted file mode 100644
index 9a16f67..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_contextvars.py
+++ /dev/null
@@ -1,310 +0,0 @@
-from __future__ import print_function
-
-import gc
-import sys
-import unittest
-
-from functools import partial
-from unittest import skipUnless
-from unittest import skipIf
-
-from greenlet import greenlet
-from greenlet import getcurrent
-from . import TestCase
-
-
-try:
- from contextvars import Context
- from contextvars import ContextVar
- from contextvars import copy_context
- # From the documentation:
- #
- # Important: Context Variables should be created at the top module
- # level and never in closures. Context objects hold strong
- # references to context variables which prevents context variables
- # from being properly garbage collected.
- ID_VAR = ContextVar("id", default=None)
- VAR_VAR = ContextVar("var", default=None)
- ContextVar = None
-except ImportError:
- Context = ContextVar = copy_context = None
-
-# We don't support testing if greenlet's built-in context var support is disabled.
-@skipUnless(Context is not None, "ContextVar not supported")
-class ContextVarsTests(TestCase):
- def _new_ctx_run(self, *args, **kwargs):
- return copy_context().run(*args, **kwargs)
-
- def _increment(self, greenlet_id, callback, counts, expect):
- ctx_var = ID_VAR
- if expect is None:
- self.assertIsNone(ctx_var.get())
- else:
- self.assertEqual(ctx_var.get(), expect)
- ctx_var.set(greenlet_id)
- for _ in range(2):
- counts[ctx_var.get()] += 1
- callback()
-
- def _test_context(self, propagate_by):
- # pylint:disable=too-many-branches
- ID_VAR.set(0)
-
- callback = getcurrent().switch
- counts = dict((i, 0) for i in range(5))
-
- lets = [
- greenlet(partial(
- partial(
- copy_context().run,
- self._increment
- ) if propagate_by == "run" else self._increment,
- greenlet_id=i,
- callback=callback,
- counts=counts,
- expect=(
- i - 1 if propagate_by == "share" else
- 0 if propagate_by in ("set", "run") else None
- )
- ))
- for i in range(1, 5)
- ]
-
- for let in lets:
- if propagate_by == "set":
- let.gr_context = copy_context()
- elif propagate_by == "share":
- let.gr_context = getcurrent().gr_context
-
- for i in range(2):
- counts[ID_VAR.get()] += 1
- for let in lets:
- let.switch()
-
- if propagate_by == "run":
- # Must leave each context.run() in reverse order of entry
- for let in reversed(lets):
- let.switch()
- else:
- # No context.run(), so fine to exit in any order.
- for let in lets:
- let.switch()
-
- for let in lets:
- self.assertTrue(let.dead)
- # When using run(), we leave the run() as the greenlet dies,
- # and there's no context "underneath". When not using run(),
- # gr_context still reflects the context the greenlet was
- # running in.
- if propagate_by == 'run':
- self.assertIsNone(let.gr_context)
- else:
- self.assertIsNotNone(let.gr_context)
-
-
- if propagate_by == "share":
- self.assertEqual(counts, {0: 1, 1: 1, 2: 1, 3: 1, 4: 6})
- else:
- self.assertEqual(set(counts.values()), set([2]))
-
- def test_context_propagated_by_context_run(self):
- self._new_ctx_run(self._test_context, "run")
-
- def test_context_propagated_by_setting_attribute(self):
- self._new_ctx_run(self._test_context, "set")
-
- def test_context_not_propagated(self):
- self._new_ctx_run(self._test_context, None)
-
- def test_context_shared(self):
- self._new_ctx_run(self._test_context, "share")
-
- def test_break_ctxvars(self):
- let1 = greenlet(copy_context().run)
- let2 = greenlet(copy_context().run)
- let1.switch(getcurrent().switch)
- let2.switch(getcurrent().switch)
- # Since let2 entered the current context and let1 exits its own, the
- # interpreter emits:
- # RuntimeError: cannot exit context: thread state references a different context object
- let1.switch()
-
- def test_not_broken_if_using_attribute_instead_of_context_run(self):
- let1 = greenlet(getcurrent().switch)
- let2 = greenlet(getcurrent().switch)
- let1.gr_context = copy_context()
- let2.gr_context = copy_context()
- let1.switch()
- let2.switch()
- let1.switch()
- let2.switch()
-
- def test_context_assignment_while_running(self):
- # pylint:disable=too-many-statements
- ID_VAR.set(None)
-
- def target():
- self.assertIsNone(ID_VAR.get())
- self.assertIsNone(gr.gr_context)
-
- # Context is created on first use
- ID_VAR.set(1)
- self.assertIsInstance(gr.gr_context, Context)
- self.assertEqual(ID_VAR.get(), 1)
- self.assertEqual(gr.gr_context[ID_VAR], 1)
-
- # Clearing the context makes it get re-created as another
- # empty context when next used
- old_context = gr.gr_context
- gr.gr_context = None # assign None while running
- self.assertIsNone(ID_VAR.get())
- self.assertIsNone(gr.gr_context)
- ID_VAR.set(2)
- self.assertIsInstance(gr.gr_context, Context)
- self.assertEqual(ID_VAR.get(), 2)
- self.assertEqual(gr.gr_context[ID_VAR], 2)
-
- new_context = gr.gr_context
- getcurrent().parent.switch((old_context, new_context))
- # parent switches us back to old_context
-
- self.assertEqual(ID_VAR.get(), 1)
- gr.gr_context = new_context # assign non-None while running
- self.assertEqual(ID_VAR.get(), 2)
-
- getcurrent().parent.switch()
- # parent switches us back to no context
- self.assertIsNone(ID_VAR.get())
- self.assertIsNone(gr.gr_context)
- gr.gr_context = old_context
- self.assertEqual(ID_VAR.get(), 1)
-
- getcurrent().parent.switch()
- # parent switches us back to no context
- self.assertIsNone(ID_VAR.get())
- self.assertIsNone(gr.gr_context)
-
- gr = greenlet(target)
-
- with self.assertRaisesRegex(AttributeError, "can't delete context attribute"):
- del gr.gr_context
-
- self.assertIsNone(gr.gr_context)
- old_context, new_context = gr.switch()
- self.assertIs(new_context, gr.gr_context)
- self.assertEqual(old_context[ID_VAR], 1)
- self.assertEqual(new_context[ID_VAR], 2)
- self.assertEqual(new_context.run(ID_VAR.get), 2)
- gr.gr_context = old_context # assign non-None while suspended
- gr.switch()
- self.assertIs(gr.gr_context, new_context)
- gr.gr_context = None # assign None while suspended
- gr.switch()
- self.assertIs(gr.gr_context, old_context)
- gr.gr_context = None
- gr.switch()
- self.assertIsNone(gr.gr_context)
-
- # Make sure there are no reference leaks
- gr = None
- gc.collect()
- self.assertEqual(sys.getrefcount(old_context), 2)
- self.assertEqual(sys.getrefcount(new_context), 2)
-
- def test_context_assignment_different_thread(self):
- import threading
- VAR_VAR.set(None)
- ctx = Context()
-
- is_running = threading.Event()
- should_suspend = threading.Event()
- did_suspend = threading.Event()
- should_exit = threading.Event()
- holder = []
-
- def greenlet_in_thread_fn():
- VAR_VAR.set(1)
- is_running.set()
- should_suspend.wait(10)
- VAR_VAR.set(2)
- getcurrent().parent.switch()
- holder.append(VAR_VAR.get())
-
- def thread_fn():
- gr = greenlet(greenlet_in_thread_fn)
- gr.gr_context = ctx
- holder.append(gr)
- gr.switch()
- did_suspend.set()
- should_exit.wait(10)
- gr.switch()
- del gr
- greenlet() # trigger cleanup
-
- thread = threading.Thread(target=thread_fn, daemon=True)
- thread.start()
- is_running.wait(10)
- gr = holder[0]
-
- # Can't access or modify context if the greenlet is running
- # in a different thread
- with self.assertRaisesRegex(ValueError, "running in a different"):
- getattr(gr, 'gr_context')
- with self.assertRaisesRegex(ValueError, "running in a different"):
- gr.gr_context = None
-
- should_suspend.set()
- did_suspend.wait(10)
-
- # OK to access and modify context if greenlet is suspended
- self.assertIs(gr.gr_context, ctx)
- self.assertEqual(gr.gr_context[VAR_VAR], 2)
- gr.gr_context = None
-
- should_exit.set()
- thread.join(10)
-
- self.assertEqual(holder, [gr, None])
-
- # Context can still be accessed/modified when greenlet is dead:
- self.assertIsNone(gr.gr_context)
- gr.gr_context = ctx
- self.assertIs(gr.gr_context, ctx)
-
- # Otherwise we leak greenlets on some platforms.
- # XXX: Should be able to do this automatically
- del holder[:]
- gr = None
- thread = None
-
- def test_context_assignment_wrong_type(self):
- g = greenlet()
- with self.assertRaisesRegex(TypeError,
- "greenlet context must be a contextvars.Context or None"):
- g.gr_context = self
-
-
-@skipIf(Context is not None, "ContextVar supported")
-class NoContextVarsTests(TestCase):
- def test_contextvars_errors(self):
- let1 = greenlet(getcurrent().switch)
- self.assertFalse(hasattr(let1, 'gr_context'))
- with self.assertRaises(AttributeError):
- getattr(let1, 'gr_context')
-
- with self.assertRaises(AttributeError):
- let1.gr_context = None
-
- let1.switch()
-
- with self.assertRaises(AttributeError):
- getattr(let1, 'gr_context')
-
- with self.assertRaises(AttributeError):
- let1.gr_context = None
-
- del let1
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_cpp.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_cpp.py
deleted file mode 100644
index 2d0cc9c..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_cpp.py
+++ /dev/null
@@ -1,73 +0,0 @@
-from __future__ import print_function
-from __future__ import absolute_import
-
-import subprocess
-import unittest
-
-import greenlet
-from . import _test_extension_cpp
-from . import TestCase
-from . import WIN
-
-class CPPTests(TestCase):
- def test_exception_switch(self):
- greenlets = []
- for i in range(4):
- g = greenlet.greenlet(_test_extension_cpp.test_exception_switch)
- g.switch(i)
- greenlets.append(g)
- for i, g in enumerate(greenlets):
- self.assertEqual(g.switch(), i)
-
- def _do_test_unhandled_exception(self, target):
- import os
- import sys
- script = os.path.join(
- os.path.dirname(__file__),
- 'fail_cpp_exception.py',
- )
- args = [sys.executable, script, target.__name__ if not isinstance(target, str) else target]
- __traceback_info__ = args
- with self.assertRaises(subprocess.CalledProcessError) as exc:
- subprocess.check_output(
- args,
- encoding='utf-8',
- stderr=subprocess.STDOUT
- )
-
- ex = exc.exception
- expected_exit = self.get_expected_returncodes_for_aborted_process()
- self.assertIn(ex.returncode, expected_exit)
- self.assertIn('fail_cpp_exception is running', ex.output)
- return ex.output
-
-
- def test_unhandled_nonstd_exception_aborts(self):
- # verify that plain unhandled throw aborts
- self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_nonstd)
-
- def test_unhandled_std_exception_aborts(self):
- # verify that plain unhandled throw aborts
- self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_std)
-
- @unittest.skipIf(WIN, "XXX: This does not crash on Windows")
- # Meaning the exception is getting lost somewhere...
- def test_unhandled_std_exception_as_greenlet_function_aborts(self):
- # verify that plain unhandled throw aborts
- output = self._do_test_unhandled_exception('run_as_greenlet_target')
- self.assertIn(
- # We really expect this to be prefixed with "greenlet: Unhandled C++ exception:"
- # as added by our handler for std::exception (see TUserGreenlet.cpp), but
- # that's not correct everywhere --- our handler never runs before std::terminate
- # gets called (for example, on arm32).
- 'Thrown from an extension.',
- output
- )
-
- def test_unhandled_exception_in_greenlet_aborts(self):
- # verify that unhandled throw called in greenlet aborts too
- self._do_test_unhandled_exception('run_unhandled_exception_in_greenlet_aborts')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_extension_interface.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_extension_interface.py
deleted file mode 100644
index 34b6656..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_extension_interface.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from __future__ import print_function
-from __future__ import absolute_import
-
-import sys
-
-import greenlet
-from . import _test_extension
-from . import TestCase
-
-# pylint:disable=c-extension-no-member
-
-class CAPITests(TestCase):
- def test_switch(self):
- self.assertEqual(
- 50, _test_extension.test_switch(greenlet.greenlet(lambda: 50)))
-
- def test_switch_kwargs(self):
- def adder(x, y):
- return x * y
- g = greenlet.greenlet(adder)
- self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2))
-
- def test_setparent(self):
- # pylint:disable=disallowed-name
- def foo():
- def bar():
- greenlet.getcurrent().parent.switch()
-
- # This final switch should go back to the main greenlet, since
- # the test_setparent() function in the C extension should have
- # reparented this greenlet.
- greenlet.getcurrent().parent.switch()
- raise AssertionError("Should never have reached this code")
- child = greenlet.greenlet(bar)
- child.switch()
- greenlet.getcurrent().parent.switch(child)
- greenlet.getcurrent().parent.throw(
- AssertionError("Should never reach this code"))
- foo_child = greenlet.greenlet(foo).switch()
- self.assertEqual(None, _test_extension.test_setparent(foo_child))
-
- def test_getcurrent(self):
- _test_extension.test_getcurrent()
-
- def test_new_greenlet(self):
- self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15))
-
- def test_raise_greenlet_dead(self):
- self.assertRaises(
- greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet)
-
- def test_raise_greenlet_error(self):
- self.assertRaises(
- greenlet.error, _test_extension.test_raise_greenlet_error)
-
- def test_throw(self):
- seen = []
-
- def foo(): # pylint:disable=disallowed-name
- try:
- greenlet.getcurrent().parent.switch()
- except ValueError:
- seen.append(sys.exc_info()[1])
- except greenlet.GreenletExit:
- raise AssertionError
- g = greenlet.greenlet(foo)
- g.switch()
- _test_extension.test_throw(g)
- self.assertEqual(len(seen), 1)
- self.assertTrue(
- isinstance(seen[0], ValueError),
- "ValueError was not raised in foo()")
- self.assertEqual(
- str(seen[0]),
- 'take that sucka!',
- "message doesn't match")
-
- def test_non_traceback_param(self):
- with self.assertRaises(TypeError) as exc:
- _test_extension.test_throw_exact(
- greenlet.getcurrent(),
- Exception,
- Exception(),
- self
- )
- self.assertEqual(str(exc.exception),
- "throw() third argument must be a traceback object")
-
- def test_instance_of_wrong_type(self):
- with self.assertRaises(TypeError) as exc:
- _test_extension.test_throw_exact(
- greenlet.getcurrent(),
- Exception(),
- BaseException(),
- None,
- )
-
- self.assertEqual(str(exc.exception),
- "instance exception may not have a separate value")
-
- def test_not_throwable(self):
- with self.assertRaises(TypeError) as exc:
- _test_extension.test_throw_exact(
- greenlet.getcurrent(),
- "abc",
- None,
- None,
- )
- self.assertEqual(str(exc.exception),
- "exceptions must be classes, or instances, not str")
-
-
-if __name__ == '__main__':
- import unittest
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_gc.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_gc.py
deleted file mode 100644
index 994addb..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_gc.py
+++ /dev/null
@@ -1,86 +0,0 @@
-import gc
-
-import weakref
-
-import greenlet
-
-
-from . import TestCase
-from .leakcheck import fails_leakcheck
-# These only work with greenlet gc support
-# which is no longer optional.
-assert greenlet.GREENLET_USE_GC
-
-class GCTests(TestCase):
- def test_dead_circular_ref(self):
- o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch())
- gc.collect()
- if o() is not None:
- import sys
- print("O IS NOT NONE.", sys.getrefcount(o()))
- self.assertIsNone(o())
- self.assertFalse(gc.garbage, gc.garbage)
-
- def test_circular_greenlet(self):
- class circular_greenlet(greenlet.greenlet):
- self = None
- o = circular_greenlet()
- o.self = o
- o = weakref.ref(o)
- gc.collect()
- self.assertIsNone(o())
- self.assertFalse(gc.garbage, gc.garbage)
-
- def test_inactive_ref(self):
- class inactive_greenlet(greenlet.greenlet):
- def __init__(self):
- greenlet.greenlet.__init__(self, run=self.run)
-
- def run(self):
- pass
- o = inactive_greenlet()
- o = weakref.ref(o)
- gc.collect()
- self.assertIsNone(o())
- self.assertFalse(gc.garbage, gc.garbage)
-
- @fails_leakcheck
- def test_finalizer_crash(self):
- # This test is designed to crash when active greenlets
- # are made garbage collectable, until the underlying
- # problem is resolved. How does it work:
- # - order of object creation is important
- # - array is created first, so it is moved to unreachable first
- # - we create a cycle between a greenlet and this array
- # - we create an object that participates in gc, is only
- # referenced by a greenlet, and would corrupt gc lists
- # on destruction, the easiest is to use an object with
- # a finalizer
- # - because array is the first object in unreachable it is
- # cleared first, which causes all references to greenlet
- # to disappear and causes greenlet to be destroyed, but since
- # it is still live it causes a switch during gc, which causes
- # an object with finalizer to be destroyed, which causes stack
- # corruption and then a crash
-
- class object_with_finalizer(object):
- def __del__(self):
- pass
- array = []
- parent = greenlet.getcurrent()
- def greenlet_body():
- greenlet.getcurrent().object = object_with_finalizer()
- try:
- parent.switch()
- except greenlet.GreenletExit:
- print("Got greenlet exit!")
- finally:
- del greenlet.getcurrent().object
- g = greenlet.greenlet(greenlet_body)
- g.array = array
- array.append(g)
- g.switch()
- del array
- del g
- greenlet.getcurrent()
- gc.collect()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_generator.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_generator.py
deleted file mode 100644
index ca4a644..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_generator.py
+++ /dev/null
@@ -1,59 +0,0 @@
-
-from greenlet import greenlet
-
-from . import TestCase
-
-class genlet(greenlet):
- parent = None
- def __init__(self, *args, **kwds):
- self.args = args
- self.kwds = kwds
-
- def run(self):
- fn, = self.fn
- fn(*self.args, **self.kwds)
-
- def __iter__(self):
- return self
-
- def __next__(self):
- self.parent = greenlet.getcurrent()
- result = self.switch()
- if self:
- return result
-
- raise StopIteration
-
- next = __next__
-
-
-def Yield(value):
- g = greenlet.getcurrent()
- while not isinstance(g, genlet):
- if g is None:
- raise RuntimeError('yield outside a genlet')
- g = g.parent
- g.parent.switch(value)
-
-
-def generator(func):
- class Generator(genlet):
- fn = (func,)
- return Generator
-
-# ____________________________________________________________
-
-
-class GeneratorTests(TestCase):
- def test_generator(self):
- seen = []
-
- def g(n):
- for i in range(n):
- seen.append(i)
- Yield(i)
- g = generator(g)
- for _ in range(3):
- for j in g(5):
- seen.append(j)
- self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4])
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_generator_nested.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_generator_nested.py
deleted file mode 100644
index 8d752a6..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_generator_nested.py
+++ /dev/null
@@ -1,168 +0,0 @@
-
-from greenlet import greenlet
-from . import TestCase
-from .leakcheck import fails_leakcheck
-
-class genlet(greenlet):
- parent = None
- def __init__(self, *args, **kwds):
- self.args = args
- self.kwds = kwds
- self.child = None
-
- def run(self):
- # Note the function is packed in a tuple
- # to avoid creating a bound method for it.
- fn, = self.fn
- fn(*self.args, **self.kwds)
-
- def __iter__(self):
- return self
-
- def set_child(self, child):
- self.child = child
-
- def __next__(self):
- if self.child:
- child = self.child
- while child.child:
- tmp = child
- child = child.child
- tmp.child = None
-
- result = child.switch()
- else:
- self.parent = greenlet.getcurrent()
- result = self.switch()
-
- if self:
- return result
-
- raise StopIteration
-
- next = __next__
-
-def Yield(value, level=1):
- g = greenlet.getcurrent()
-
- while level != 0:
- if not isinstance(g, genlet):
- raise RuntimeError('yield outside a genlet')
- if level > 1:
- g.parent.set_child(g)
- g = g.parent
- level -= 1
-
- g.switch(value)
-
-
-def Genlet(func):
- class TheGenlet(genlet):
- fn = (func,)
- return TheGenlet
-
-# ____________________________________________________________
-
-
-def g1(n, seen):
- for i in range(n):
- seen.append(i + 1)
- yield i
-
-
-def g2(n, seen):
- for i in range(n):
- seen.append(i + 1)
- Yield(i)
-
-g2 = Genlet(g2)
-
-
-def nested(i):
- Yield(i)
-
-
-def g3(n, seen):
- for i in range(n):
- seen.append(i + 1)
- nested(i)
-g3 = Genlet(g3)
-
-
-def a(n):
- if n == 0:
- return
- for ii in ax(n - 1):
- Yield(ii)
- Yield(n)
-ax = Genlet(a)
-
-
-def perms(l):
- if len(l) > 1:
- for e in l:
- # No syntactical sugar for generator expressions
- x = [Yield([e] + p) for p in perms([x for x in l if x != e])]
- assert x
- else:
- Yield(l)
-perms = Genlet(perms)
-
-
-def gr1(n):
- for ii in range(1, n):
- Yield(ii)
- Yield(ii * ii, 2)
-
-gr1 = Genlet(gr1)
-
-
-def gr2(n, seen):
- for ii in gr1(n):
- seen.append(ii)
-
-gr2 = Genlet(gr2)
-
-
-class NestedGeneratorTests(TestCase):
- def test_layered_genlets(self):
- seen = []
- for ii in gr2(5, seen):
- seen.append(ii)
- self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16])
-
- @fails_leakcheck
- def test_permutations(self):
- gen_perms = perms(list(range(4)))
- permutations = list(gen_perms)
- self.assertEqual(len(permutations), 4 * 3 * 2 * 1)
- self.assertIn([0, 1, 2, 3], permutations)
- self.assertIn([3, 2, 1, 0], permutations)
- res = []
- for ii in zip(perms(list(range(4))), perms(list(range(3)))):
- res.append(ii)
- self.assertEqual(
- res,
- [([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]),
- ([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]),
- ([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])])
- # XXX Test to make sure we are working as a generator expression
-
- def test_genlet_simple(self):
- for g in g1, g2, g3:
- seen = []
- for _ in range(3):
- for j in g(5, seen):
- seen.append(j)
- self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4])
-
- def test_genlet_bad(self):
- try:
- Yield(10)
- except RuntimeError:
- pass
-
- def test_nested_genlets(self):
- seen = []
- for ii in ax(5):
- seen.append(ii)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet.py
deleted file mode 100644
index 51849cd..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet.py
+++ /dev/null
@@ -1,1311 +0,0 @@
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import gc
-import sys
-import time
-import threading
-
-from abc import ABCMeta, abstractmethod
-
-import greenlet
-from greenlet import greenlet as RawGreenlet
-from . import TestCase
-from .leakcheck import fails_leakcheck
-
-
-# We manually manage locks in many tests
-# pylint:disable=consider-using-with
-# pylint:disable=too-many-public-methods
-# This module is quite large.
-# TODO: Refactor into separate test files. For example,
-# put all the regression tests that used to produce
-# crashes in test_greenlet_no_crash; put tests that DO deliberately crash
-# the interpreter into test_greenlet_crash.
-# pylint:disable=too-many-lines
-
-class SomeError(Exception):
- pass
-
-
-def fmain(seen):
- try:
- greenlet.getcurrent().parent.switch()
- except:
- seen.append(sys.exc_info()[0])
- raise
- raise SomeError
-
-
-def send_exception(g, exc):
- # note: send_exception(g, exc) can be now done with g.throw(exc).
- # the purpose of this test is to explicitly check the propagation rules.
- def crasher(exc):
- raise exc
- g1 = RawGreenlet(crasher, parent=g)
- g1.switch(exc)
-
-
-class TestGreenlet(TestCase):
-
- def _do_simple_test(self):
- lst = []
-
- def f():
- lst.append(1)
- greenlet.getcurrent().parent.switch()
- lst.append(3)
- g = RawGreenlet(f)
- lst.append(0)
- g.switch()
- lst.append(2)
- g.switch()
- lst.append(4)
- self.assertEqual(lst, list(range(5)))
-
- def test_simple(self):
- self._do_simple_test()
-
- def test_switch_no_run_raises_AttributeError(self):
- g = RawGreenlet()
- with self.assertRaises(AttributeError) as exc:
- g.switch()
-
- self.assertIn("run", str(exc.exception))
-
- def test_throw_no_run_raises_AttributeError(self):
- g = RawGreenlet()
- with self.assertRaises(AttributeError) as exc:
- g.throw(SomeError)
-
- self.assertIn("run", str(exc.exception))
-
- def test_parent_equals_None(self):
- g = RawGreenlet(parent=None)
- self.assertIsNotNone(g)
- self.assertIs(g.parent, greenlet.getcurrent())
-
- def test_run_equals_None(self):
- g = RawGreenlet(run=None)
- self.assertIsNotNone(g)
- self.assertIsNone(g.run)
-
- def test_two_children(self):
- lst = []
-
- def f():
- lst.append(1)
- greenlet.getcurrent().parent.switch()
- lst.extend([1, 1])
- g = RawGreenlet(f)
- h = RawGreenlet(f)
- g.switch()
- self.assertEqual(len(lst), 1)
- h.switch()
- self.assertEqual(len(lst), 2)
- h.switch()
- self.assertEqual(len(lst), 4)
- self.assertEqual(h.dead, True)
- g.switch()
- self.assertEqual(len(lst), 6)
- self.assertEqual(g.dead, True)
-
- def test_two_recursive_children(self):
- lst = []
-
- def f():
- lst.append('b')
- greenlet.getcurrent().parent.switch()
-
- def g():
- lst.append('a')
- g = RawGreenlet(f)
- g.switch()
- lst.append('c')
-
- g = RawGreenlet(g)
- self.assertEqual(sys.getrefcount(g), 2)
- g.switch()
- self.assertEqual(lst, ['a', 'b', 'c'])
- # Just the one in this frame, plus the one on the stack we pass to the function
- self.assertEqual(sys.getrefcount(g), 2)
-
- def test_threads(self):
- success = []
-
- def f():
- self._do_simple_test()
- success.append(True)
- ths = [threading.Thread(target=f) for i in range(10)]
- for th in ths:
- th.start()
- for th in ths:
- th.join(10)
- self.assertEqual(len(success), len(ths))
-
- def test_exception(self):
- seen = []
- g1 = RawGreenlet(fmain)
- g2 = RawGreenlet(fmain)
- g1.switch(seen)
- g2.switch(seen)
- g2.parent = g1
-
- self.assertEqual(seen, [])
- #with self.assertRaises(SomeError):
- # p("***Switching back")
- # g2.switch()
- # Creating this as a bound method can reveal bugs that
- # are hidden on newer versions of Python that avoid creating
- # bound methods for direct expressions; IOW, don't use the `with`
- # form!
- self.assertRaises(SomeError, g2.switch)
- self.assertEqual(seen, [SomeError])
-
- value = g2.switch()
- self.assertEqual(value, ())
- self.assertEqual(seen, [SomeError])
-
- value = g2.switch(25)
- self.assertEqual(value, 25)
- self.assertEqual(seen, [SomeError])
-
-
- def test_send_exception(self):
- seen = []
- g1 = RawGreenlet(fmain)
- g1.switch(seen)
- self.assertRaises(KeyError, send_exception, g1, KeyError)
- self.assertEqual(seen, [KeyError])
-
- def test_dealloc(self):
- seen = []
- g1 = RawGreenlet(fmain)
- g2 = RawGreenlet(fmain)
- g1.switch(seen)
- g2.switch(seen)
- self.assertEqual(seen, [])
- del g1
- gc.collect()
- self.assertEqual(seen, [greenlet.GreenletExit])
- del g2
- gc.collect()
- self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit])
-
- def test_dealloc_catches_GreenletExit_throws_other(self):
- def run():
- try:
- greenlet.getcurrent().parent.switch()
- except greenlet.GreenletExit:
- raise SomeError from None
-
- g = RawGreenlet(run)
- g.switch()
- # Destroying the only reference to the greenlet causes it
- # to get GreenletExit; when it in turn raises, even though we're the parent
- # we don't get the exception, it just gets printed.
- # When we run on 3.8 only, we can use sys.unraisablehook
- oldstderr = sys.stderr
- try:
- from cStringIO import StringIO
- except ImportError:
- from io import StringIO
- stderr = sys.stderr = StringIO()
- try:
- del g
- finally:
- sys.stderr = oldstderr
-
- v = stderr.getvalue()
- self.assertIn("Exception", v)
- self.assertIn('ignored', v)
- self.assertIn("SomeError", v)
-
-
- def test_dealloc_other_thread(self):
- seen = []
- someref = []
-
- bg_glet_created_running_and_no_longer_ref_in_bg = threading.Event()
- fg_ref_released = threading.Event()
- bg_should_be_clear = threading.Event()
- ok_to_exit_bg_thread = threading.Event()
-
- def f():
- g1 = RawGreenlet(fmain)
- g1.switch(seen)
- someref.append(g1)
- del g1
- gc.collect()
-
- bg_glet_created_running_and_no_longer_ref_in_bg.set()
- fg_ref_released.wait(3)
-
- RawGreenlet() # trigger release
- bg_should_be_clear.set()
- ok_to_exit_bg_thread.wait(3)
- RawGreenlet() # One more time
-
- t = threading.Thread(target=f)
- t.start()
- bg_glet_created_running_and_no_longer_ref_in_bg.wait(10)
-
- self.assertEqual(seen, [])
- self.assertEqual(len(someref), 1)
- del someref[:]
- gc.collect()
- # g1 is not released immediately because it's from another thread
- self.assertEqual(seen, [])
- fg_ref_released.set()
- bg_should_be_clear.wait(3)
- try:
- self.assertEqual(seen, [greenlet.GreenletExit])
- finally:
- ok_to_exit_bg_thread.set()
- t.join(10)
- del seen[:]
- del someref[:]
-
- def test_frame(self):
- def f1():
- f = sys._getframe(0) # pylint:disable=protected-access
- self.assertEqual(f.f_back, None)
- greenlet.getcurrent().parent.switch(f)
- return "meaning of life"
- g = RawGreenlet(f1)
- frame = g.switch()
- self.assertTrue(frame is g.gr_frame)
- self.assertTrue(g)
-
- from_g = g.switch()
- self.assertFalse(g)
- self.assertEqual(from_g, 'meaning of life')
- self.assertEqual(g.gr_frame, None)
-
- def test_thread_bug(self):
- def runner(x):
- g = RawGreenlet(lambda: time.sleep(x))
- g.switch()
- t1 = threading.Thread(target=runner, args=(0.2,))
- t2 = threading.Thread(target=runner, args=(0.3,))
- t1.start()
- t2.start()
- t1.join(10)
- t2.join(10)
-
- def test_switch_kwargs(self):
- def run(a, b):
- self.assertEqual(a, 4)
- self.assertEqual(b, 2)
- return 42
- x = RawGreenlet(run).switch(a=4, b=2)
- self.assertEqual(x, 42)
-
- def test_switch_kwargs_to_parent(self):
- def run(x):
- greenlet.getcurrent().parent.switch(x=x)
- greenlet.getcurrent().parent.switch(2, x=3)
- return x, x ** 2
- g = RawGreenlet(run)
- self.assertEqual({'x': 3}, g.switch(3))
- self.assertEqual(((2,), {'x': 3}), g.switch())
- self.assertEqual((3, 9), g.switch())
-
- def test_switch_to_another_thread(self):
- data = {}
- created_event = threading.Event()
- done_event = threading.Event()
-
- def run():
- data['g'] = RawGreenlet(lambda: None)
- created_event.set()
- done_event.wait(10)
- thread = threading.Thread(target=run)
- thread.start()
- created_event.wait(10)
- with self.assertRaises(greenlet.error):
- data['g'].switch()
- done_event.set()
- thread.join(10)
- # XXX: Should handle this automatically
- data.clear()
-
- def test_exc_state(self):
- def f():
- try:
- raise ValueError('fun')
- except: # pylint:disable=bare-except
- exc_info = sys.exc_info()
- RawGreenlet(h).switch()
- self.assertEqual(exc_info, sys.exc_info())
-
- def h():
- self.assertEqual(sys.exc_info(), (None, None, None))
-
- RawGreenlet(f).switch()
-
- def test_instance_dict(self):
- def f():
- greenlet.getcurrent().test = 42
- def deldict(g):
- del g.__dict__
- def setdict(g, value):
- g.__dict__ = value
- g = RawGreenlet(f)
- self.assertEqual(g.__dict__, {})
- g.switch()
- self.assertEqual(g.test, 42)
- self.assertEqual(g.__dict__, {'test': 42})
- g.__dict__ = g.__dict__
- self.assertEqual(g.__dict__, {'test': 42})
- self.assertRaises(TypeError, deldict, g)
- self.assertRaises(TypeError, setdict, g, 42)
-
- def test_running_greenlet_has_no_run(self):
- has_run = []
- def func():
- has_run.append(
- hasattr(greenlet.getcurrent(), 'run')
- )
-
- g = RawGreenlet(func)
- g.switch()
- self.assertEqual(has_run, [False])
-
- def test_deepcopy(self):
- import copy
- self.assertRaises(TypeError, copy.copy, RawGreenlet())
- self.assertRaises(TypeError, copy.deepcopy, RawGreenlet())
-
- def test_parent_restored_on_kill(self):
- hub = RawGreenlet(lambda: None)
- main = greenlet.getcurrent()
- result = []
- def worker():
- try:
- # Wait to be killed by going back to the test.
- main.switch()
- except greenlet.GreenletExit:
- # Resurrect and switch to parent
- result.append(greenlet.getcurrent().parent)
- result.append(greenlet.getcurrent())
- hub.switch()
- g = RawGreenlet(worker, parent=hub)
- g.switch()
- # delete the only reference, thereby raising GreenletExit
- del g
- self.assertTrue(result)
- self.assertIs(result[0], main)
- self.assertIs(result[1].parent, hub)
- # Delete them, thereby breaking the cycle between the greenlet
- # and the frame, which otherwise would never be collectable
- # XXX: We should be able to automatically fix this.
- del result[:]
- hub = None
- main = None
-
- def test_parent_return_failure(self):
- # No run causes AttributeError on switch
- g1 = RawGreenlet()
- # Greenlet that implicitly switches to parent
- g2 = RawGreenlet(lambda: None, parent=g1)
- # AttributeError should propagate to us, no fatal errors
- with self.assertRaises(AttributeError):
- g2.switch()
-
- def test_throw_exception_not_lost(self):
- class mygreenlet(RawGreenlet):
- def __getattribute__(self, name):
- try:
- raise Exception # pylint:disable=broad-exception-raised
- except: # pylint:disable=bare-except
- pass
- return RawGreenlet.__getattribute__(self, name)
- g = mygreenlet(lambda: None)
- self.assertRaises(SomeError, g.throw, SomeError())
-
- @fails_leakcheck
- def _do_test_throw_to_dead_thread_doesnt_crash(self, wait_for_cleanup=False):
- result = []
- def worker():
- greenlet.getcurrent().parent.switch()
-
- def creator():
- g = RawGreenlet(worker)
- g.switch()
- result.append(g)
- if wait_for_cleanup:
- # Let this greenlet eventually be cleaned up.
- g.switch()
- greenlet.getcurrent()
- t = threading.Thread(target=creator)
- t.start()
- t.join(10)
- del t
- # But, depending on the operating system, the thread
- # deallocator may not actually have run yet! So we can't be
- # sure about the error message unless we wait.
- if wait_for_cleanup:
- self.wait_for_pending_cleanups()
- with self.assertRaises(greenlet.error) as exc:
- result[0].throw(SomeError)
-
- if not wait_for_cleanup:
- self.assertIn(
- str(exc.exception), [
- "cannot switch to a different thread (which happens to have exited)",
- "cannot switch to a different thread"
- ]
- )
- else:
- self.assertEqual(
- str(exc.exception),
- "cannot switch to a different thread (which happens to have exited)",
- )
-
- if hasattr(result[0].gr_frame, 'clear'):
- # The frame is actually executing (it thinks), we can't clear it.
- with self.assertRaises(RuntimeError):
- result[0].gr_frame.clear()
- # Unfortunately, this doesn't actually clear the references, they're in the
- # fast local array.
- if not wait_for_cleanup:
- result[0].gr_frame.f_locals.clear()
- else:
- self.assertIsNone(result[0].gr_frame)
-
- del creator
- worker = None
- del result[:]
- # XXX: we ought to be able to automatically fix this.
- # See issue 252
- self.expect_greenlet_leak = True # direct us not to wait for it to go away
-
- @fails_leakcheck
- def test_throw_to_dead_thread_doesnt_crash(self):
- self._do_test_throw_to_dead_thread_doesnt_crash()
-
- def test_throw_to_dead_thread_doesnt_crash_wait(self):
- self._do_test_throw_to_dead_thread_doesnt_crash(True)
-
- @fails_leakcheck
- def test_recursive_startup(self):
- class convoluted(RawGreenlet):
- def __init__(self):
- RawGreenlet.__init__(self)
- self.count = 0
- def __getattribute__(self, name):
- if name == 'run' and self.count == 0:
- self.count = 1
- self.switch(43)
- return RawGreenlet.__getattribute__(self, name)
- def run(self, value):
- while True:
- self.parent.switch(value)
- g = convoluted()
- self.assertEqual(g.switch(42), 43)
- # Exits the running greenlet, otherwise it leaks
- # XXX: We should be able to automatically fix this
- #g.throw(greenlet.GreenletExit)
- #del g
- self.expect_greenlet_leak = True
-
- def test_threaded_updatecurrent(self):
- # released when main thread should execute
- lock1 = threading.Lock()
- lock1.acquire()
- # released when another thread should execute
- lock2 = threading.Lock()
- lock2.acquire()
- class finalized(object):
- def __del__(self):
- # happens while in green_updatecurrent() in main greenlet
- # should be very careful not to accidentally call it again
- # at the same time we must make sure another thread executes
- lock2.release()
- lock1.acquire()
- # now ts_current belongs to another thread
- def deallocator():
- greenlet.getcurrent().parent.switch()
- def fthread():
- lock2.acquire()
- greenlet.getcurrent()
- del g[0]
- lock1.release()
- lock2.acquire()
- greenlet.getcurrent()
- lock1.release()
- main = greenlet.getcurrent()
- g = [RawGreenlet(deallocator)]
- g[0].bomb = finalized()
- g[0].switch()
- t = threading.Thread(target=fthread)
- t.start()
- # let another thread grab ts_current and deallocate g[0]
- lock2.release()
- lock1.acquire()
- # this is the corner stone
- # getcurrent() will notice that ts_current belongs to another thread
- # and start the update process, which would notice that g[0] should
- # be deallocated, and that will execute an object's finalizer. Now,
- # that object will let another thread run so it can grab ts_current
- # again, which would likely crash the interpreter if there's no
- # check for this case at the end of green_updatecurrent(). This test
- # passes if getcurrent() returns correct result, but it's likely
- # to randomly crash if it's not anyway.
- self.assertEqual(greenlet.getcurrent(), main)
- # wait for another thread to complete, just in case
- t.join(10)
-
- def test_dealloc_switch_args_not_lost(self):
- seen = []
- def worker():
- # wait for the value
- value = greenlet.getcurrent().parent.switch()
- # delete all references to ourself
- del worker[0]
- initiator.parent = greenlet.getcurrent().parent
- # switch to main with the value, but because
- # ts_current is the last reference to us we
- # return here immediately, where we resurrect ourself.
- try:
- greenlet.getcurrent().parent.switch(value)
- finally:
- seen.append(greenlet.getcurrent())
- def initiator():
- return 42 # implicitly falls thru to parent
-
- worker = [RawGreenlet(worker)]
-
- worker[0].switch() # prime worker
- initiator = RawGreenlet(initiator, worker[0])
- value = initiator.switch()
- self.assertTrue(seen)
- self.assertEqual(value, 42)
-
- def test_tuple_subclass(self):
- # The point of this test is to see what happens when a custom
- # tuple subclass is used as an object passed directly to the C
- # function ``green_switch``; part of ``green_switch`` checks
- # the ``len()`` of the ``args`` tuple, and that can call back
- # into Python. Here, when it calls back into Python, we
- # recursively enter ``green_switch`` again.
-
- # This test is really only relevant on Python 2. The builtin
- # `apply` function directly passes the given args tuple object
- # to the underlying function, whereas the Python 3 version
- # unpacks and repacks into an actual tuple. This could still
- # happen using the C API on Python 3 though. We should write a
- # builtin version of apply() ourself.
- def _apply(func, a, k):
- func(*a, **k)
-
- class mytuple(tuple):
- def __len__(self):
- greenlet.getcurrent().switch()
- return tuple.__len__(self)
- args = mytuple()
- kwargs = dict(a=42)
- def switchapply():
- _apply(greenlet.getcurrent().parent.switch, args, kwargs)
- g = RawGreenlet(switchapply)
- self.assertEqual(g.switch(), kwargs)
-
- def test_abstract_subclasses(self):
- AbstractSubclass = ABCMeta(
- 'AbstractSubclass',
- (RawGreenlet,),
- {'run': abstractmethod(lambda self: None)})
-
- class BadSubclass(AbstractSubclass):
- pass
-
- class GoodSubclass(AbstractSubclass):
- def run(self):
- pass
-
- GoodSubclass() # should not raise
- self.assertRaises(TypeError, BadSubclass)
-
- def test_implicit_parent_with_threads(self):
- if not gc.isenabled():
- return # cannot test with disabled gc
- N = gc.get_threshold()[0]
- if N < 50:
- return # cannot test with such a small N
- def attempt():
- lock1 = threading.Lock()
- lock1.acquire()
- lock2 = threading.Lock()
- lock2.acquire()
- recycled = [False]
- def another_thread():
- lock1.acquire() # wait for gc
- greenlet.getcurrent() # update ts_current
- lock2.release() # release gc
- t = threading.Thread(target=another_thread)
- t.start()
- class gc_callback(object):
- def __del__(self):
- lock1.release()
- lock2.acquire()
- recycled[0] = True
- class garbage(object):
- def __init__(self):
- self.cycle = self
- self.callback = gc_callback()
- l = []
- x = range(N*2)
- current = greenlet.getcurrent()
- g = garbage()
- for _ in x:
- g = None # lose reference to garbage
- if recycled[0]:
- # gc callback called prematurely
- t.join(10)
- return False
- last = RawGreenlet()
- if recycled[0]:
- break # yes! gc called in green_new
- l.append(last) # increase allocation counter
- else:
- # gc callback not called when expected
- gc.collect()
- if recycled[0]:
- t.join(10)
- return False
- self.assertEqual(last.parent, current)
- for g in l:
- self.assertEqual(g.parent, current)
- return True
- for _ in range(5):
- if attempt():
- break
-
- def test_issue_245_reference_counting_subclass_no_threads(self):
- # https://github.com/python-greenlet/greenlet/issues/245
- # Before the fix, this crashed pretty reliably on
- # Python 3.10, at least on macOS; but much less reliably on other
- # interpreters (memory layout must have changed).
- # The threaded test crashed more reliably on more interpreters.
- from greenlet import getcurrent
- from greenlet import GreenletExit
-
- class Greenlet(RawGreenlet):
- pass
-
- initial_refs = sys.getrefcount(Greenlet)
- # This has to be an instance variable because
- # Python 2 raises a SyntaxError if we delete a local
- # variable referenced in an inner scope.
- self.glets = [] # pylint:disable=attribute-defined-outside-init
-
- def greenlet_main():
- try:
- getcurrent().parent.switch()
- except GreenletExit:
- self.glets.append(getcurrent())
-
- # Before the
- for _ in range(10):
- Greenlet(greenlet_main).switch()
-
- del self.glets
- self.assertEqual(sys.getrefcount(Greenlet), initial_refs)
-
- def test_issue_245_reference_counting_subclass_threads(self):
- # https://github.com/python-greenlet/greenlet/issues/245
- from threading import Thread
- from threading import Event
-
- from greenlet import getcurrent
-
- class MyGreenlet(RawGreenlet):
- pass
-
- glets = []
- ref_cleared = Event()
-
- def greenlet_main():
- getcurrent().parent.switch()
-
- def thread_main(greenlet_running_event):
- mine = MyGreenlet(greenlet_main)
- glets.append(mine)
- # The greenlets being deleted must be active
- mine.switch()
- # Don't keep any reference to it in this thread
- del mine
- # Let main know we published our greenlet.
- greenlet_running_event.set()
- # Wait for main to let us know the references are
- # gone and the greenlet objects no longer reachable
- ref_cleared.wait(10)
- # The creating thread must call getcurrent() (or a few other
- # greenlet APIs) because that's when the thread-local list of dead
- # greenlets gets cleared.
- getcurrent()
-
- # We start with 3 references to the subclass:
- # - This module
- # - Its __mro__
- # - The __subclassess__ attribute of greenlet
- # - (If we call gc.get_referents(), we find four entries, including
- # some other tuple ``(greenlet)`` that I'm not sure about but must be part
- # of the machinery.)
- #
- # On Python 3.10 it's often enough to just run 3 threads; on Python 2.7,
- # more threads are needed, and the results are still
- # non-deterministic. Presumably the memory layouts are different
- initial_refs = sys.getrefcount(MyGreenlet)
- thread_ready_events = []
- for _ in range(
- initial_refs + 45
- ):
- event = Event()
- thread = Thread(target=thread_main, args=(event,))
- thread_ready_events.append(event)
- thread.start()
-
-
- for done_event in thread_ready_events:
- done_event.wait(10)
-
-
- del glets[:]
- ref_cleared.set()
- # Let any other thread run; it will crash the interpreter
- # if not fixed (or silently corrupt memory and we possibly crash
- # later).
- self.wait_for_pending_cleanups()
- self.assertEqual(sys.getrefcount(MyGreenlet), initial_refs)
-
- def test_falling_off_end_switches_to_unstarted_parent_raises_error(self):
- def no_args():
- return 13
-
- parent_never_started = RawGreenlet(no_args)
-
- def leaf():
- return 42
-
- child = RawGreenlet(leaf, parent_never_started)
-
- # Because the run function takes to arguments
- with self.assertRaises(TypeError):
- child.switch()
-
- def test_falling_off_end_switches_to_unstarted_parent_works(self):
- def one_arg(x):
- return (x, 24)
-
- parent_never_started = RawGreenlet(one_arg)
-
- def leaf():
- return 42
-
- child = RawGreenlet(leaf, parent_never_started)
-
- result = child.switch()
- self.assertEqual(result, (42, 24))
-
- def test_switch_to_dead_greenlet_with_unstarted_perverse_parent(self):
- class Parent(RawGreenlet):
- def __getattribute__(self, name):
- if name == 'run':
- raise SomeError
-
-
- parent_never_started = Parent()
- seen = []
- child = RawGreenlet(lambda: seen.append(42), parent_never_started)
- # Because we automatically start the parent when the child is
- # finished
- with self.assertRaises(SomeError):
- child.switch()
-
- self.assertEqual(seen, [42])
-
- with self.assertRaises(SomeError):
- child.switch()
- self.assertEqual(seen, [42])
-
- def test_switch_to_dead_greenlet_reparent(self):
- seen = []
- parent_never_started = RawGreenlet(lambda: seen.append(24))
- child = RawGreenlet(lambda: seen.append(42))
-
- child.switch()
- self.assertEqual(seen, [42])
-
- child.parent = parent_never_started
- # This actually is the same as switching to the parent.
- result = child.switch()
- self.assertIsNone(result)
- self.assertEqual(seen, [42, 24])
-
- def test_can_access_f_back_of_suspended_greenlet(self):
- # This tests our frame rewriting to work around Python 3.12+ having
- # some interpreter frames on the C stack. It will crash in the absence
- # of that logic.
- main = greenlet.getcurrent()
-
- def outer():
- inner()
-
- def inner():
- main.switch(sys._getframe(0))
-
- hub = RawGreenlet(outer)
- # start it
- hub.switch()
-
- # start another greenlet to make sure we aren't relying on
- # anything in `hub` still being on the C stack
- unrelated = RawGreenlet(lambda: None)
- unrelated.switch()
-
- # now it is suspended
- self.assertIsNotNone(hub.gr_frame)
- self.assertEqual(hub.gr_frame.f_code.co_name, "inner")
- self.assertIsNotNone(hub.gr_frame.f_back)
- self.assertEqual(hub.gr_frame.f_back.f_code.co_name, "outer")
- # The next line is what would crash
- self.assertIsNone(hub.gr_frame.f_back.f_back)
-
- def test_get_stack_with_nested_c_calls(self):
- from functools import partial
- from . import _test_extension_cpp
-
- def recurse(v):
- if v > 0:
- return v * _test_extension_cpp.test_call(partial(recurse, v - 1))
- return greenlet.getcurrent().parent.switch()
-
- gr = RawGreenlet(recurse)
- gr.switch(5)
- frame = gr.gr_frame
- for i in range(5):
- self.assertEqual(frame.f_locals["v"], i)
- frame = frame.f_back
- self.assertEqual(frame.f_locals["v"], 5)
- self.assertIsNone(frame.f_back)
- self.assertEqual(gr.switch(10), 1200) # 1200 = 5! * 10
-
- def test_frames_always_exposed(self):
- # On Python 3.12 this will crash if we don't set the
- # gr_frames_always_exposed attribute. More background:
- # https://github.com/python-greenlet/greenlet/issues/388
- main = greenlet.getcurrent()
-
- def outer():
- inner(sys._getframe(0))
-
- def inner(frame):
- main.switch(frame)
-
- gr = RawGreenlet(outer)
- frame = gr.switch()
-
- # Do something else to clobber the part of the C stack used by `gr`,
- # so we can't skate by on "it just happened to still be there"
- unrelated = RawGreenlet(lambda: None)
- unrelated.switch()
-
- self.assertEqual(frame.f_code.co_name, "outer")
- # The next line crashes on 3.12 if we haven't exposed the frames.
- self.assertIsNone(frame.f_back)
-
-
-class TestGreenletSetParentErrors(TestCase):
- def test_threaded_reparent(self):
- data = {}
- created_event = threading.Event()
- done_event = threading.Event()
-
- def run():
- data['g'] = RawGreenlet(lambda: None)
- created_event.set()
- done_event.wait(10)
-
- def blank():
- greenlet.getcurrent().parent.switch()
-
- thread = threading.Thread(target=run)
- thread.start()
- created_event.wait(10)
- g = RawGreenlet(blank)
- g.switch()
- with self.assertRaises(ValueError) as exc:
- g.parent = data['g']
- done_event.set()
- thread.join(10)
-
- self.assertEqual(str(exc.exception), "parent cannot be on a different thread")
-
- def test_unexpected_reparenting(self):
- another = []
- def worker():
- g = RawGreenlet(lambda: None)
- another.append(g)
- g.switch()
- t = threading.Thread(target=worker)
- t.start()
- t.join(10)
- # The first time we switch (running g_initialstub(), which is
- # when we look up the run attribute) we attempt to change the
- # parent to one from another thread (which also happens to be
- # dead). ``g_initialstub()`` should detect this and raise a
- # greenlet error.
- #
- # EXCEPT: With the fix for #252, this is actually detected
- # sooner, when setting the parent itself. Prior to that fix,
- # the main greenlet from the background thread kept a valid
- # value for ``run_info``, and appeared to be a valid parent
- # until we actually started the greenlet. But now that it's
- # cleared, this test is catching whether ``green_setparent``
- # can detect the dead thread.
- #
- # Further refactoring once again changes this back to a greenlet.error
- #
- # We need to wait for the cleanup to happen, but we're
- # deliberately leaking a main greenlet here.
- self.wait_for_pending_cleanups(initial_main_greenlets=self.main_greenlets_before_test + 1)
-
- class convoluted(RawGreenlet):
- def __getattribute__(self, name):
- if name == 'run':
- self.parent = another[0] # pylint:disable=attribute-defined-outside-init
- return RawGreenlet.__getattribute__(self, name)
- g = convoluted(lambda: None)
- with self.assertRaises(greenlet.error) as exc:
- g.switch()
- self.assertEqual(str(exc.exception),
- "cannot switch to a different thread (which happens to have exited)")
- del another[:]
-
- def test_unexpected_reparenting_thread_running(self):
- # Like ``test_unexpected_reparenting``, except the background thread is
- # actually still alive.
- another = []
- switched_to_greenlet = threading.Event()
- keep_main_alive = threading.Event()
- def worker():
- g = RawGreenlet(lambda: None)
- another.append(g)
- g.switch()
- switched_to_greenlet.set()
- keep_main_alive.wait(10)
- class convoluted(RawGreenlet):
- def __getattribute__(self, name):
- if name == 'run':
- self.parent = another[0] # pylint:disable=attribute-defined-outside-init
- return RawGreenlet.__getattribute__(self, name)
-
- t = threading.Thread(target=worker)
- t.start()
-
- switched_to_greenlet.wait(10)
- try:
- g = convoluted(lambda: None)
-
- with self.assertRaises(greenlet.error) as exc:
- g.switch()
- self.assertEqual(str(exc.exception), "cannot switch to a different thread")
- finally:
- keep_main_alive.set()
- t.join(10)
- # XXX: Should handle this automatically.
- del another[:]
-
- def test_cannot_delete_parent(self):
- worker = RawGreenlet(lambda: None)
- self.assertIs(worker.parent, greenlet.getcurrent())
-
- with self.assertRaises(AttributeError) as exc:
- del worker.parent
- self.assertEqual(str(exc.exception), "can't delete attribute")
-
- def test_cannot_delete_parent_of_main(self):
- with self.assertRaises(AttributeError) as exc:
- del greenlet.getcurrent().parent
- self.assertEqual(str(exc.exception), "can't delete attribute")
-
-
- def test_main_greenlet_parent_is_none(self):
- # assuming we're in a main greenlet here.
- self.assertIsNone(greenlet.getcurrent().parent)
-
- def test_set_parent_wrong_types(self):
- def bg():
- # Go back to main.
- greenlet.getcurrent().parent.switch()
-
- def check(glet):
- for p in None, 1, self, "42":
- with self.assertRaises(TypeError) as exc:
- glet.parent = p
-
- self.assertEqual(
- str(exc.exception),
- "GreenletChecker: Expected any type of greenlet, not " + type(p).__name__)
-
- # First, not running
- g = RawGreenlet(bg)
- self.assertFalse(g)
- check(g)
-
- # Then when running.
- g.switch()
- self.assertTrue(g)
- check(g)
-
- # Let it finish
- g.switch()
-
-
- def test_trivial_cycle(self):
- glet = RawGreenlet(lambda: None)
- with self.assertRaises(ValueError) as exc:
- glet.parent = glet
- self.assertEqual(str(exc.exception), "cyclic parent chain")
-
- def test_trivial_cycle_main(self):
- # This used to produce a ValueError, but we catch it earlier than that now.
- with self.assertRaises(AttributeError) as exc:
- greenlet.getcurrent().parent = greenlet.getcurrent()
- self.assertEqual(str(exc.exception), "cannot set the parent of a main greenlet")
-
- def test_deeper_cycle(self):
- g1 = RawGreenlet(lambda: None)
- g2 = RawGreenlet(lambda: None)
- g3 = RawGreenlet(lambda: None)
-
- g1.parent = g2
- g2.parent = g3
- with self.assertRaises(ValueError) as exc:
- g3.parent = g1
- self.assertEqual(str(exc.exception), "cyclic parent chain")
-
-
-class TestRepr(TestCase):
-
- def assertEndsWith(self, got, suffix):
- self.assertTrue(got.endswith(suffix), (got, suffix))
-
- def test_main_while_running(self):
- r = repr(greenlet.getcurrent())
- self.assertEndsWith(r, " current active started main>")
-
- def test_main_in_background(self):
- main = greenlet.getcurrent()
- def run():
- return repr(main)
-
- g = RawGreenlet(run)
- r = g.switch()
- self.assertEndsWith(r, ' suspended active started main>')
-
- def test_initial(self):
- r = repr(RawGreenlet())
- self.assertEndsWith(r, ' pending>')
-
- def test_main_from_other_thread(self):
- main = greenlet.getcurrent()
-
- class T(threading.Thread):
- original_main = thread_main = None
- main_glet = None
- def run(self):
- self.original_main = repr(main)
- self.main_glet = greenlet.getcurrent()
- self.thread_main = repr(self.main_glet)
-
- t = T()
- t.start()
- t.join(10)
-
- self.assertEndsWith(t.original_main, ' suspended active started main>')
- self.assertEndsWith(t.thread_main, ' current active started main>')
- # give the machinery time to notice the death of the thread,
- # and clean it up. Note that we don't use
- # ``expect_greenlet_leak`` or wait_for_pending_cleanups,
- # because at this point we know we have an extra greenlet
- # still reachable.
- for _ in range(3):
- time.sleep(0.001)
-
- # In the past, main greenlets, even from dead threads, never
- # really appear dead. We have fixed that, and we also report
- # that the thread is dead in the repr. (Do this multiple times
- # to make sure that we don't self-modify and forget our state
- # in the C++ code).
- for _ in range(3):
- self.assertTrue(t.main_glet.dead)
- r = repr(t.main_glet)
- self.assertEndsWith(r, ' (thread exited) dead>')
-
- def test_dead(self):
- g = RawGreenlet(lambda: None)
- g.switch()
- self.assertEndsWith(repr(g), ' dead>')
- self.assertNotIn('suspended', repr(g))
- self.assertNotIn('started', repr(g))
- self.assertNotIn('active', repr(g))
-
- def test_formatting_produces_native_str(self):
- # https://github.com/python-greenlet/greenlet/issues/218
- # %s formatting on Python 2 was producing unicode, not str.
-
- g_dead = RawGreenlet(lambda: None)
- g_not_started = RawGreenlet(lambda: None)
- g_cur = greenlet.getcurrent()
-
- for g in g_dead, g_not_started, g_cur:
-
- self.assertIsInstance(
- '%s' % (g,),
- str
- )
- self.assertIsInstance(
- '%r' % (g,),
- str,
- )
-
-
-class TestMainGreenlet(TestCase):
- # Tests some implementation details, and relies on some
- # implementation details.
-
- def _check_current_is_main(self):
- # implementation detail
- assert 'main' in repr(greenlet.getcurrent())
-
- t = type(greenlet.getcurrent())
- assert 'main' not in repr(t)
- return t
-
- def test_main_greenlet_type_can_be_subclassed(self):
- main_type = self._check_current_is_main()
- subclass = type('subclass', (main_type,), {})
- self.assertIsNotNone(subclass)
-
- def test_main_greenlet_is_greenlet(self):
- self._check_current_is_main()
- self.assertIsInstance(greenlet.getcurrent(), RawGreenlet)
-
-
-
-class TestBrokenGreenlets(TestCase):
- # Tests for things that used to, or still do, terminate the interpreter.
- # This often means doing unsavory things.
-
- def test_failed_to_initialstub(self):
- def func():
- raise AssertionError("Never get here")
-
-
- g = greenlet._greenlet.UnswitchableGreenlet(func)
- g.force_switch_error = True
-
- with self.assertRaisesRegex(SystemError,
- "Failed to switch stacks into a greenlet for the first time."):
- g.switch()
-
- def test_failed_to_switch_into_running(self):
- runs = []
- def func():
- runs.append(1)
- greenlet.getcurrent().parent.switch()
- runs.append(2)
- greenlet.getcurrent().parent.switch()
- runs.append(3) # pragma: no cover
-
- g = greenlet._greenlet.UnswitchableGreenlet(func)
- g.switch()
- self.assertEqual(runs, [1])
- g.switch()
- self.assertEqual(runs, [1, 2])
- g.force_switch_error = True
-
- with self.assertRaisesRegex(SystemError,
- "Failed to switch stacks into a running greenlet."):
- g.switch()
-
- # If we stopped here, we would fail the leakcheck, because we've left
- # the ``inner_bootstrap()`` C frame and its descendents hanging around,
- # which have a bunch of Python references. They'll never get cleaned up
- # if we don't let the greenlet finish.
- g.force_switch_error = False
- g.switch()
- self.assertEqual(runs, [1, 2, 3])
-
- def test_failed_to_slp_switch_into_running(self):
- ex = self.assertScriptRaises('fail_slp_switch.py')
-
- self.assertIn('fail_slp_switch is running', ex.output)
- self.assertIn(ex.returncode, self.get_expected_returncodes_for_aborted_process())
-
- def test_reentrant_switch_two_greenlets(self):
- # Before we started capturing the arguments in g_switch_finish, this could crash.
- output = self.run_script('fail_switch_two_greenlets.py')
- self.assertIn('In g1_run', output)
- self.assertIn('TRACE', output)
- self.assertIn('LEAVE TRACE', output)
- self.assertIn('Falling off end of main', output)
- self.assertIn('Falling off end of g1_run', output)
- self.assertIn('Falling off end of g2', output)
-
- def test_reentrant_switch_three_greenlets(self):
- # On debug builds of greenlet, this used to crash with an assertion error;
- # on non-debug versions, it ran fine (which it should not do!).
- # Now it always crashes correctly with a TypeError
- ex = self.assertScriptRaises('fail_switch_three_greenlets.py', exitcodes=(1,))
-
- self.assertIn('TypeError', ex.output)
- self.assertIn('positional arguments', ex.output)
-
- def test_reentrant_switch_three_greenlets2(self):
- # This actually passed on debug and non-debug builds. It
- # should probably have been triggering some debug assertions
- # but it didn't.
- #
- # I think the fixes for the above test also kicked in here.
- output = self.run_script('fail_switch_three_greenlets2.py')
- self.assertIn(
- "RESULTS: [('trace', 'switch'), "
- "('trace', 'switch'), ('g2 arg', 'g2 from tracefunc'), "
- "('trace', 'switch'), ('main g1', 'from g2_run'), ('trace', 'switch'), "
- "('g1 arg', 'g1 from main'), ('trace', 'switch'), ('main g2', 'from g1_run'), "
- "('trace', 'switch'), ('g1 from parent', 'g1 from main 2'), ('trace', 'switch'), "
- "('main g1.2', 'g1 done'), ('trace', 'switch'), ('g2 from parent', ()), "
- "('trace', 'switch'), ('main g2.2', 'g2 done')]",
- output
- )
-
- def test_reentrant_switch_GreenletAlreadyStartedInPython(self):
- output = self.run_script('fail_initialstub_already_started.py')
-
- self.assertIn(
- "RESULTS: ['Begin C', 'Switch to b from B.__getattribute__ in C', "
- "('Begin B', ()), '_B_run switching to main', ('main from c', 'From B'), "
- "'B.__getattribute__ back from main in C', ('Begin A', (None,)), "
- "('A dead?', True, 'B dead?', True, 'C dead?', False), "
- "'C done', ('main from c.2', None)]",
- output
- )
-
- def test_reentrant_switch_run_callable_has_del(self):
- output = self.run_script('fail_clearing_run_switches.py')
- self.assertIn(
- "RESULTS ["
- "('G.__getattribute__', 'run'), ('RunCallable', '__del__'), "
- "('main: g.switch()', 'from RunCallable'), ('run_func', 'enter')"
- "]",
- output
- )
-
-if __name__ == '__main__':
- import unittest
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet_trash.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet_trash.py
deleted file mode 100644
index 8d9716e..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_greenlet_trash.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Tests for greenlets interacting with the CPython trash can API.
-
-The CPython trash can API is not designed to be re-entered from a
-single thread. But this can happen using greenlets, if something
-during the object deallocation process switches greenlets, and this second
-greenlet then causes the trash can to get entered again. Here, we do this
-very explicitly, but in other cases (like gevent) it could be arbitrarily more
-complicated: for example, a weakref callback might try to acquire a lock that's
-already held by another greenlet; that would allow a greenlet switch to occur.
-
-See https://github.com/gevent/gevent/issues/1909
-
-This test is fragile and relies on details of the CPython
-implementation (like most of the rest of this package):
-
- - We enter the trashcan and deferred deallocation after
- ``_PyTrash_UNWIND_LEVEL`` calls. This constant, defined in
- CPython's object.c, is generally 50. That's basically how many objects are required to
- get us into the deferred deallocation situation.
-
- - The test fails by hitting an ``assert()`` in object.c; if the
- build didn't enable assert, then we don't catch this.
-
- - If the test fails in that way, the interpreter crashes.
-"""
-from __future__ import print_function, absolute_import, division
-
-import unittest
-
-class TestTrashCanReEnter(unittest.TestCase):
-
- def test_it(self):
- # Try several times to trigger it, because it isn't 100%
- # reliable.
- for _ in range(10):
- self.check_it()
-
- def check_it(self): # pylint:disable=too-many-statements
- import greenlet
- from greenlet._greenlet import get_tstate_trash_delete_nesting # pylint:disable=no-name-in-module
-
- main = greenlet.getcurrent()
-
- assert get_tstate_trash_delete_nesting() == 0
-
- # We expect to be in deferred deallocation after this many
- # deallocations have occurred. TODO: I wish we had a better way to do
- # this --- that was before get_tstate_trash_delete_nesting; perhaps
- # we can use that API to do better?
- TRASH_UNWIND_LEVEL = 50
- # How many objects to put in a container; it's the container that
- # queues objects for deferred deallocation.
- OBJECTS_PER_CONTAINER = 500
-
- class Dealloc: # define the class here because we alter class variables each time we run.
- """
- An object with a ``__del__`` method. When it starts getting deallocated
- from a deferred trash can run, it switches greenlets, allocates more objects
- which then also go in the trash can. If we don't save state appropriately,
- nesting gets out of order and we can crash the interpreter.
- """
-
- #: Has our deallocation actually run and switched greenlets?
- #: When it does, this will be set to the current greenlet. This should
- #: be happening in the main greenlet, so we check that down below.
- SPAWNED = False
-
- #: Has the background greenlet run?
- BG_RAN = False
-
- BG_GLET = None
-
- #: How many of these things have ever been allocated.
- CREATED = 0
-
- #: How many of these things have ever been deallocated.
- DESTROYED = 0
-
- #: How many were destroyed not in the main greenlet. There should always
- #: be some.
- #: If the test is broken or things change in the trashcan implementation,
- #: this may not be correct.
- DESTROYED_BG = 0
-
- def __init__(self, sequence_number):
- """
- :param sequence_number: The ordinal of this object during
- one particular creation run. This is used to detect (guess, really)
- when we have entered the trash can's deferred deallocation.
- """
- self.i = sequence_number
- Dealloc.CREATED += 1
-
- def __del__(self):
- if self.i == TRASH_UNWIND_LEVEL and not self.SPAWNED:
- Dealloc.SPAWNED = greenlet.getcurrent()
- other = Dealloc.BG_GLET = greenlet.greenlet(background_greenlet)
- x = other.switch()
- assert x == 42
- # It's important that we don't switch back to the greenlet,
- # we leave it hanging there in an incomplete state. But we don't let it
- # get collected, either. If we complete it now, while we're still
- # in the scope of the initial trash can, things work out and we
- # don't see the problem. We need this greenlet to complete
- # at some point in the future, after we've exited this trash can invocation.
- del other
- elif self.i == 40 and greenlet.getcurrent() is not main:
- Dealloc.BG_RAN = True
- try:
- main.switch(42)
- except greenlet.GreenletExit as ex:
- # We expect this; all references to us go away
- # while we're still running, and we need to finish deleting
- # ourself.
- Dealloc.BG_RAN = type(ex)
- del ex
-
- # Record the fact that we're dead last of all. This ensures that
- # we actually get returned too.
- Dealloc.DESTROYED += 1
- if greenlet.getcurrent() is not main:
- Dealloc.DESTROYED_BG += 1
-
-
- def background_greenlet():
- # We direct through a second function, instead of
- # directly calling ``make_some()``, so that we have complete
- # control over when these objects are destroyed: we need them
- # to be destroyed in the context of the background greenlet
- t = make_some()
- del t # Triggere deletion.
-
- def make_some():
- t = ()
- i = OBJECTS_PER_CONTAINER
- while i:
- # Nest the tuples; it's the recursion that gets us
- # into trash.
- t = (Dealloc(i), t)
- i -= 1
- return t
-
-
- some = make_some()
- self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER)
- self.assertEqual(Dealloc.DESTROYED, 0)
-
- # If we're going to crash, it should be on the following line.
- # We only crash if ``assert()`` is enabled, of course.
- del some
-
- # For non-debug builds of CPython, we won't crash. The best we can do is check
- # the nesting level explicitly.
- self.assertEqual(0, get_tstate_trash_delete_nesting())
-
- # Discard this, raising GreenletExit into where it is waiting.
- Dealloc.BG_GLET = None
- # The same nesting level maintains.
- self.assertEqual(0, get_tstate_trash_delete_nesting())
-
- # We definitely cleaned some up in the background
- self.assertGreater(Dealloc.DESTROYED_BG, 0)
-
- # Make sure all the cleanups happened.
- self.assertIs(Dealloc.SPAWNED, main)
- self.assertTrue(Dealloc.BG_RAN)
- self.assertEqual(Dealloc.BG_RAN, greenlet.GreenletExit)
- self.assertEqual(Dealloc.CREATED, Dealloc.DESTROYED )
- self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER * 2)
-
- import gc
- gc.collect()
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_leaks.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_leaks.py
deleted file mode 100644
index ed1fa71..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_leaks.py
+++ /dev/null
@@ -1,443 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Testing scenarios that may have leaked.
-"""
-from __future__ import print_function, absolute_import, division
-
-import sys
-import gc
-
-import time
-import weakref
-import threading
-
-
-import greenlet
-from . import TestCase
-from .leakcheck import fails_leakcheck
-from .leakcheck import ignores_leakcheck
-from .leakcheck import RUNNING_ON_MANYLINUX
-
-# pylint:disable=protected-access
-
-assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0
-
-class HasFinalizerTracksInstances(object):
- EXTANT_INSTANCES = set()
- def __init__(self, msg):
- self.msg = sys.intern(msg)
- self.EXTANT_INSTANCES.add(id(self))
- def __del__(self):
- self.EXTANT_INSTANCES.remove(id(self))
- def __repr__(self):
- return "<HasFinalizerTracksInstances at 0x%x %r>" % (
- id(self), self.msg
- )
- @classmethod
- def reset(cls):
- cls.EXTANT_INSTANCES.clear()
-
-
-class TestLeaks(TestCase):
-
- def test_arg_refs(self):
- args = ('a', 'b', 'c')
- refcount_before = sys.getrefcount(args)
- # pylint:disable=unnecessary-lambda
- g = greenlet.greenlet(
- lambda *args: greenlet.getcurrent().parent.switch(*args))
- for _ in range(100):
- g.switch(*args)
- self.assertEqual(sys.getrefcount(args), refcount_before)
-
- def test_kwarg_refs(self):
- kwargs = {}
- # pylint:disable=unnecessary-lambda
- g = greenlet.greenlet(
- lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs))
- for _ in range(100):
- g.switch(**kwargs)
- self.assertEqual(sys.getrefcount(kwargs), 2)
-
-
- @staticmethod
- def __recycle_threads():
- # By introducing a thread that does sleep we allow other threads,
- # that have triggered their __block condition, but did not have a
- # chance to deallocate their thread state yet, to finally do so.
- # The way it works is by requiring a GIL switch (different thread),
- # which does a GIL release (sleep), which might do a GIL switch
- # to finished threads and allow them to clean up.
- def worker():
- time.sleep(0.001)
- t = threading.Thread(target=worker)
- t.start()
- time.sleep(0.001)
- t.join(10)
-
- def test_threaded_leak(self):
- gg = []
- def worker():
- # only main greenlet present
- gg.append(weakref.ref(greenlet.getcurrent()))
- for _ in range(2):
- t = threading.Thread(target=worker)
- t.start()
- t.join(10)
- del t
- greenlet.getcurrent() # update ts_current
- self.__recycle_threads()
- greenlet.getcurrent() # update ts_current
- gc.collect()
- greenlet.getcurrent() # update ts_current
- for g in gg:
- self.assertIsNone(g())
-
- def test_threaded_adv_leak(self):
- gg = []
- def worker():
- # main and additional *finished* greenlets
- ll = greenlet.getcurrent().ll = []
- def additional():
- ll.append(greenlet.getcurrent())
- for _ in range(2):
- greenlet.greenlet(additional).switch()
- gg.append(weakref.ref(greenlet.getcurrent()))
- for _ in range(2):
- t = threading.Thread(target=worker)
- t.start()
- t.join(10)
- del t
- greenlet.getcurrent() # update ts_current
- self.__recycle_threads()
- greenlet.getcurrent() # update ts_current
- gc.collect()
- greenlet.getcurrent() # update ts_current
- for g in gg:
- self.assertIsNone(g())
-
- def assertClocksUsed(self):
- used = greenlet._greenlet.get_clocks_used_doing_optional_cleanup()
- self.assertGreaterEqual(used, 0)
- # we don't lose the value
- greenlet._greenlet.enable_optional_cleanup(True)
- used2 = greenlet._greenlet.get_clocks_used_doing_optional_cleanup()
- self.assertEqual(used, used2)
- self.assertGreater(greenlet._greenlet.CLOCKS_PER_SEC, 1)
-
- def _check_issue251(self,
- manually_collect_background=True,
- explicit_reference_to_switch=False):
- # See https://github.com/python-greenlet/greenlet/issues/251
- # Killing a greenlet (probably not the main one)
- # in one thread from another thread would
- # result in leaking a list (the ts_delkey list).
- # We no longer use lists to hold that stuff, though.
-
- # For the test to be valid, even empty lists have to be tracked by the
- # GC
-
- assert gc.is_tracked([])
- HasFinalizerTracksInstances.reset()
- greenlet.getcurrent()
- greenlets_before = self.count_objects(greenlet.greenlet, exact_kind=False)
-
- background_glet_running = threading.Event()
- background_glet_killed = threading.Event()
- background_greenlets = []
-
- # XXX: Switching this to a greenlet subclass that overrides
- # run results in all callers failing the leaktest; that
- # greenlet instance is leaked. There's a bound method for
- # run() living on the stack of the greenlet in g_initialstub,
- # and since we don't manually switch back to the background
- # greenlet to let it "fall off the end" and exit the
- # g_initialstub function, it never gets cleaned up. Making the
- # garbage collector aware of this bound method (making it an
- # attribute of the greenlet structure and traversing into it)
- # doesn't help, for some reason.
- def background_greenlet():
- # Throw control back to the main greenlet.
- jd = HasFinalizerTracksInstances("DELETING STACK OBJECT")
- greenlet._greenlet.set_thread_local(
- 'test_leaks_key',
- HasFinalizerTracksInstances("DELETING THREAD STATE"))
- # Explicitly keeping 'switch' in a local variable
- # breaks this test in all versions
- if explicit_reference_to_switch:
- s = greenlet.getcurrent().parent.switch
- s([jd])
- else:
- greenlet.getcurrent().parent.switch([jd])
-
- bg_main_wrefs = []
-
- def background_thread():
- glet = greenlet.greenlet(background_greenlet)
- bg_main_wrefs.append(weakref.ref(glet.parent))
-
- background_greenlets.append(glet)
- glet.switch() # Be sure it's active.
- # Control is ours again.
- del glet # Delete one reference from the thread it runs in.
- background_glet_running.set()
- background_glet_killed.wait(10)
-
- # To trigger the background collection of the dead
- # greenlet, thus clearing out the contents of the list, we
- # need to run some APIs. See issue 252.
- if manually_collect_background:
- greenlet.getcurrent()
-
-
- t = threading.Thread(target=background_thread)
- t.start()
- background_glet_running.wait(10)
- greenlet.getcurrent()
- lists_before = self.count_objects(list, exact_kind=True)
-
- assert len(background_greenlets) == 1
- self.assertFalse(background_greenlets[0].dead)
- # Delete the last reference to the background greenlet
- # from a different thread. This puts it in the background thread's
- # ts_delkey list.
- del background_greenlets[:]
- background_glet_killed.set()
-
- # Now wait for the background thread to die.
- t.join(10)
- del t
- # As part of the fix for 252, we need to cycle the ceval.c
- # interpreter loop to be sure it has had a chance to process
- # the pending call.
- self.wait_for_pending_cleanups()
-
- lists_after = self.count_objects(list, exact_kind=True)
- greenlets_after = self.count_objects(greenlet.greenlet, exact_kind=False)
-
- # On 2.7, we observe that lists_after is smaller than
- # lists_before. No idea what lists got cleaned up. All the
- # Python 3 versions match exactly.
- self.assertLessEqual(lists_after, lists_before)
- # On versions after 3.6, we've successfully cleaned up the
- # greenlet references thanks to the internal "vectorcall"
- # protocol; prior to that, there is a reference path through
- # the ``greenlet.switch`` method still on the stack that we
- # can't reach to clean up. The C code goes through terrific
- # lengths to clean that up.
- if not explicit_reference_to_switch \
- and greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None:
- # If cleanup was disabled, though, we may not find it.
- self.assertEqual(greenlets_after, greenlets_before)
- if manually_collect_background:
- # TODO: Figure out how to make this work!
- # The one on the stack is still leaking somehow
- # in the non-manually-collect state.
- self.assertEqual(HasFinalizerTracksInstances.EXTANT_INSTANCES, set())
- else:
- # The explicit reference prevents us from collecting it
- # and it isn't always found by the GC either for some
- # reason. The entire frame is leaked somehow, on some
- # platforms (e.g., MacPorts builds of Python (all
- # versions!)), but not on other platforms (the linux and
- # windows builds on GitHub actions and Appveyor). So we'd
- # like to write a test that proves that the main greenlet
- # sticks around, and we can on my machine (macOS 11.6,
- # MacPorts builds of everything) but we can't write that
- # same test on other platforms. However, hopefully iteration
- # done by leakcheck will find it.
- pass
-
- if greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None:
- self.assertClocksUsed()
-
- def test_issue251_killing_cross_thread_leaks_list(self):
- self._check_issue251()
-
- def test_issue251_with_cleanup_disabled(self):
- greenlet._greenlet.enable_optional_cleanup(False)
- try:
- self._check_issue251()
- finally:
- greenlet._greenlet.enable_optional_cleanup(True)
-
- @fails_leakcheck
- def test_issue251_issue252_need_to_collect_in_background(self):
- # Between greenlet 1.1.2 and the next version, this was still
- # failing because the leak of the list still exists when we
- # don't call a greenlet API before exiting the thread. The
- # proximate cause is that neither of the two greenlets from
- # the background thread are actually being destroyed, even
- # though the GC is in fact visiting both objects. It's not
- # clear where that leak is? For some reason the thread-local
- # dict holding it isn't being cleaned up.
- #
- # The leak, I think, is in the CPYthon internal function that
- # calls into green_switch(). The argument tuple is still on
- # the C stack somewhere and can't be reached? That doesn't
- # make sense, because the tuple should be collectable when
- # this object goes away.
- #
- # Note that this test sometimes spuriously passes on Linux,
- # for some reason, but I've never seen it pass on macOS.
- self._check_issue251(manually_collect_background=False)
-
- @fails_leakcheck
- def test_issue251_issue252_need_to_collect_in_background_cleanup_disabled(self):
- self.expect_greenlet_leak = True
- greenlet._greenlet.enable_optional_cleanup(False)
- try:
- self._check_issue251(manually_collect_background=False)
- finally:
- greenlet._greenlet.enable_optional_cleanup(True)
-
- @fails_leakcheck
- def test_issue251_issue252_explicit_reference_not_collectable(self):
- self._check_issue251(
- manually_collect_background=False,
- explicit_reference_to_switch=True)
-
- UNTRACK_ATTEMPTS = 100
-
- def _only_test_some_versions(self):
- # We're only looking for this problem specifically on 3.11,
- # and this set of tests is relatively fragile, depending on
- # OS and memory management details. So we want to run it on 3.11+
- # (obviously) but not every older 3.x version in order to reduce
- # false negatives. At the moment, those false results seem to have
- # resolved, so we are actually running this on 3.8+
- assert sys.version_info[0] >= 3
- if sys.version_info[:2] < (3, 8):
- self.skipTest('Only observed on 3.11')
- if RUNNING_ON_MANYLINUX:
- self.skipTest("Slow and not worth repeating here")
-
- @ignores_leakcheck
- # Because we're just trying to track raw memory, not objects, and running
- # the leakcheck makes an already slow test slower.
- def test_untracked_memory_doesnt_increase(self):
- # See https://github.com/gevent/gevent/issues/1924
- # and https://github.com/python-greenlet/greenlet/issues/328
- self._only_test_some_versions()
- def f():
- return 1
-
- ITER = 10000
- def run_it():
- for _ in range(ITER):
- greenlet.greenlet(f).switch()
-
- # Establish baseline
- for _ in range(3):
- run_it()
-
- # uss: (Linux, macOS, Windows): aka "Unique Set Size", this is
- # the memory which is unique to a process and which would be
- # freed if the process was terminated right now.
- uss_before = self.get_process_uss()
-
- for count in range(self.UNTRACK_ATTEMPTS):
- uss_before = max(uss_before, self.get_process_uss())
- run_it()
-
- uss_after = self.get_process_uss()
- if uss_after <= uss_before and count > 1:
- break
-
- self.assertLessEqual(uss_after, uss_before)
-
- def _check_untracked_memory_thread(self, deallocate_in_thread=True):
- self._only_test_some_versions()
- # Like the above test, but what if there are a bunch of
- # unfinished greenlets in a thread that dies?
- # Does it matter if we deallocate in the thread or not?
- EXIT_COUNT = [0]
-
- def f():
- try:
- greenlet.getcurrent().parent.switch()
- except greenlet.GreenletExit:
- EXIT_COUNT[0] += 1
- raise
- return 1
-
- ITER = 10000
- def run_it():
- glets = []
- for _ in range(ITER):
- # Greenlet starts, switches back to us.
- # We keep a strong reference to the greenlet though so it doesn't
- # get a GreenletExit exception.
- g = greenlet.greenlet(f)
- glets.append(g)
- g.switch()
-
- return glets
-
- test = self
-
- class ThreadFunc:
- uss_before = uss_after = 0
- glets = ()
- ITER = 2
- def __call__(self):
- self.uss_before = test.get_process_uss()
-
- for _ in range(self.ITER):
- self.glets += tuple(run_it())
-
- for g in self.glets:
- test.assertIn('suspended active', str(g))
- # Drop them.
- if deallocate_in_thread:
- self.glets = ()
- self.uss_after = test.get_process_uss()
-
- # Establish baseline
- uss_before = uss_after = None
- for count in range(self.UNTRACK_ATTEMPTS):
- EXIT_COUNT[0] = 0
- thread_func = ThreadFunc()
- t = threading.Thread(target=thread_func)
- t.start()
- t.join(30)
- self.assertFalse(t.is_alive())
-
- if uss_before is None:
- uss_before = thread_func.uss_before
-
- uss_before = max(uss_before, thread_func.uss_before)
- if deallocate_in_thread:
- self.assertEqual(thread_func.glets, ())
- self.assertEqual(EXIT_COUNT[0], ITER * thread_func.ITER)
-
- del thread_func # Deallocate the greenlets; but this won't raise into them
- del t
- if not deallocate_in_thread:
- self.assertEqual(EXIT_COUNT[0], 0)
- if deallocate_in_thread:
- self.wait_for_pending_cleanups()
-
- uss_after = self.get_process_uss()
- # See if we achieve a non-growth state at some point. Break when we do.
- if uss_after <= uss_before and count > 1:
- break
-
- self.wait_for_pending_cleanups()
- uss_after = self.get_process_uss()
- self.assertLessEqual(uss_after, uss_before, "after attempts %d" % (count,))
-
- @ignores_leakcheck
- # Because we're just trying to track raw memory, not objects, and running
- # the leakcheck makes an already slow test slower.
- def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_thread(self):
- self._check_untracked_memory_thread(deallocate_in_thread=True)
-
- @ignores_leakcheck
- # Because the main greenlets from the background threads do not exit in a timely fashion,
- # we fail the object-based leakchecks.
- def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main(self):
- self._check_untracked_memory_thread(deallocate_in_thread=False)
-
-if __name__ == '__main__':
- __import__('unittest').main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_stack_saved.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_stack_saved.py
deleted file mode 100644
index b362bf9..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_stack_saved.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import greenlet
-from . import TestCase
-
-
-class Test(TestCase):
-
- def test_stack_saved(self):
- main = greenlet.getcurrent()
- self.assertEqual(main._stack_saved, 0)
-
- def func():
- main.switch(main._stack_saved)
-
- g = greenlet.greenlet(func)
- x = g.switch()
- self.assertGreater(x, 0)
- self.assertGreater(g._stack_saved, 0)
- g.switch()
- self.assertEqual(g._stack_saved, 0)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_throw.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_throw.py
deleted file mode 100644
index f4f9a14..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_throw.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import sys
-
-
-from greenlet import greenlet
-from . import TestCase
-
-def switch(*args):
- return greenlet.getcurrent().parent.switch(*args)
-
-
-class ThrowTests(TestCase):
- def test_class(self):
- def f():
- try:
- switch("ok")
- except RuntimeError:
- switch("ok")
- return
- switch("fail")
- g = greenlet(f)
- res = g.switch()
- self.assertEqual(res, "ok")
- res = g.throw(RuntimeError)
- self.assertEqual(res, "ok")
-
- def test_val(self):
- def f():
- try:
- switch("ok")
- except RuntimeError:
- val = sys.exc_info()[1]
- if str(val) == "ciao":
- switch("ok")
- return
- switch("fail")
-
- g = greenlet(f)
- res = g.switch()
- self.assertEqual(res, "ok")
- res = g.throw(RuntimeError("ciao"))
- self.assertEqual(res, "ok")
-
- g = greenlet(f)
- res = g.switch()
- self.assertEqual(res, "ok")
- res = g.throw(RuntimeError, "ciao")
- self.assertEqual(res, "ok")
-
- def test_kill(self):
- def f():
- switch("ok")
- switch("fail")
- g = greenlet(f)
- res = g.switch()
- self.assertEqual(res, "ok")
- res = g.throw()
- self.assertTrue(isinstance(res, greenlet.GreenletExit))
- self.assertTrue(g.dead)
- res = g.throw() # immediately eaten by the already-dead greenlet
- self.assertTrue(isinstance(res, greenlet.GreenletExit))
-
- def test_throw_goes_to_original_parent(self):
- main = greenlet.getcurrent()
-
- def f1():
- try:
- main.switch("f1 ready to catch")
- except IndexError:
- return "caught"
- return "normal exit"
-
- def f2():
- main.switch("from f2")
-
- g1 = greenlet(f1)
- g2 = greenlet(f2, parent=g1)
- with self.assertRaises(IndexError):
- g2.throw(IndexError)
- self.assertTrue(g2.dead)
- self.assertTrue(g1.dead)
-
- g1 = greenlet(f1)
- g2 = greenlet(f2, parent=g1)
- res = g1.switch()
- self.assertEqual(res, "f1 ready to catch")
- res = g2.throw(IndexError)
- self.assertEqual(res, "caught")
- self.assertTrue(g2.dead)
- self.assertTrue(g1.dead)
-
- g1 = greenlet(f1)
- g2 = greenlet(f2, parent=g1)
- res = g1.switch()
- self.assertEqual(res, "f1 ready to catch")
- res = g2.switch()
- self.assertEqual(res, "from f2")
- res = g2.throw(IndexError)
- self.assertEqual(res, "caught")
- self.assertTrue(g2.dead)
- self.assertTrue(g1.dead)
-
- def test_non_traceback_param(self):
- with self.assertRaises(TypeError) as exc:
- greenlet.getcurrent().throw(
- Exception,
- Exception(),
- self
- )
- self.assertEqual(str(exc.exception),
- "throw() third argument must be a traceback object")
-
- def test_instance_of_wrong_type(self):
- with self.assertRaises(TypeError) as exc:
- greenlet.getcurrent().throw(
- Exception(),
- BaseException()
- )
-
- self.assertEqual(str(exc.exception),
- "instance exception may not have a separate value")
-
- def test_not_throwable(self):
- with self.assertRaises(TypeError) as exc:
- greenlet.getcurrent().throw(
- "abc"
- )
- self.assertEqual(str(exc.exception),
- "exceptions must be classes, or instances, not str")
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_tracing.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_tracing.py
deleted file mode 100644
index c044d4b..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_tracing.py
+++ /dev/null
@@ -1,291 +0,0 @@
-from __future__ import print_function
-import sys
-import greenlet
-import unittest
-
-from . import TestCase
-from . import PY312
-
-# https://discuss.python.org/t/cpython-3-12-greenlet-and-tracing-profiling-how-to-not-crash-and-get-correct-results/33144/2
-DEBUG_BUILD_PY312 = (
- PY312 and hasattr(sys, 'gettotalrefcount'),
- "Broken on debug builds of Python 3.12"
-)
-
-class SomeError(Exception):
- pass
-
-class GreenletTracer(object):
- oldtrace = None
-
- def __init__(self, error_on_trace=False):
- self.actions = []
- self.error_on_trace = error_on_trace
-
- def __call__(self, *args):
- self.actions.append(args)
- if self.error_on_trace:
- raise SomeError
-
- def __enter__(self):
- self.oldtrace = greenlet.settrace(self)
- return self.actions
-
- def __exit__(self, *args):
- greenlet.settrace(self.oldtrace)
-
-
-class TestGreenletTracing(TestCase):
- """
- Tests of ``greenlet.settrace()``
- """
-
- def test_a_greenlet_tracing(self):
- main = greenlet.getcurrent()
- def dummy():
- pass
- def dummyexc():
- raise SomeError()
-
- with GreenletTracer() as actions:
- g1 = greenlet.greenlet(dummy)
- g1.switch()
- g2 = greenlet.greenlet(dummyexc)
- self.assertRaises(SomeError, g2.switch)
-
- self.assertEqual(actions, [
- ('switch', (main, g1)),
- ('switch', (g1, main)),
- ('switch', (main, g2)),
- ('throw', (g2, main)),
- ])
-
- def test_b_exception_disables_tracing(self):
- main = greenlet.getcurrent()
- def dummy():
- main.switch()
- g = greenlet.greenlet(dummy)
- g.switch()
- with GreenletTracer(error_on_trace=True) as actions:
- self.assertRaises(SomeError, g.switch)
- self.assertEqual(greenlet.gettrace(), None)
-
- self.assertEqual(actions, [
- ('switch', (main, g)),
- ])
-
- def test_set_same_tracer_twice(self):
- # https://github.com/python-greenlet/greenlet/issues/332
- # Our logic in asserting that the tracefunction should
- # gain a reference was incorrect if the same tracefunction was set
- # twice.
- tracer = GreenletTracer()
- with tracer:
- greenlet.settrace(tracer)
-
-
-class PythonTracer(object):
- oldtrace = None
-
- def __init__(self):
- self.actions = []
-
- def __call__(self, frame, event, arg):
- # Record the co_name so we have an idea what function we're in.
- self.actions.append((event, frame.f_code.co_name))
-
- def __enter__(self):
- self.oldtrace = sys.setprofile(self)
- return self.actions
-
- def __exit__(self, *args):
- sys.setprofile(self.oldtrace)
-
-def tpt_callback():
- return 42
-
-class TestPythonTracing(TestCase):
- """
- Tests of the interaction of ``sys.settrace()``
- with greenlet facilities.
-
- NOTE: Most of this is probably CPython specific.
- """
-
- maxDiff = None
-
- def test_trace_events_trivial(self):
- with PythonTracer() as actions:
- tpt_callback()
- # If we use the sys.settrace instead of setprofile, we get
- # this:
-
- # self.assertEqual(actions, [
- # ('call', 'tpt_callback'),
- # ('call', '__exit__'),
- # ])
-
- self.assertEqual(actions, [
- ('return', '__enter__'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('call', '__exit__'),
- ('c_call', '__exit__'),
- ])
-
- def _trace_switch(self, glet):
- with PythonTracer() as actions:
- glet.switch()
- return actions
-
- def _check_trace_events_func_already_set(self, glet):
- actions = self._trace_switch(glet)
- self.assertEqual(actions, [
- ('return', '__enter__'),
- ('c_call', '_trace_switch'),
- ('call', 'run'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('return', 'run'),
- ('c_return', '_trace_switch'),
- ('call', '__exit__'),
- ('c_call', '__exit__'),
- ])
-
- def test_trace_events_into_greenlet_func_already_set(self):
- def run():
- return tpt_callback()
-
- self._check_trace_events_func_already_set(greenlet.greenlet(run))
-
- def test_trace_events_into_greenlet_subclass_already_set(self):
- class X(greenlet.greenlet):
- def run(self):
- return tpt_callback()
- self._check_trace_events_func_already_set(X())
-
- def _check_trace_events_from_greenlet_sets_profiler(self, g, tracer):
- g.switch()
- tpt_callback()
- tracer.__exit__()
- self.assertEqual(tracer.actions, [
- ('return', '__enter__'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('return', 'run'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('call', '__exit__'),
- ('c_call', '__exit__'),
- ])
-
-
- def test_trace_events_from_greenlet_func_sets_profiler(self):
- tracer = PythonTracer()
- def run():
- tracer.__enter__()
- return tpt_callback()
-
- self._check_trace_events_from_greenlet_sets_profiler(greenlet.greenlet(run),
- tracer)
-
- def test_trace_events_from_greenlet_subclass_sets_profiler(self):
- tracer = PythonTracer()
- class X(greenlet.greenlet):
- def run(self):
- tracer.__enter__()
- return tpt_callback()
-
- self._check_trace_events_from_greenlet_sets_profiler(X(), tracer)
-
- @unittest.skipIf(*DEBUG_BUILD_PY312)
- def test_trace_events_multiple_greenlets_switching(self):
- tracer = PythonTracer()
-
- g1 = None
- g2 = None
-
- def g1_run():
- tracer.__enter__()
- tpt_callback()
- g2.switch()
- tpt_callback()
- return 42
-
- def g2_run():
- tpt_callback()
- tracer.__exit__()
- tpt_callback()
- g1.switch()
-
- g1 = greenlet.greenlet(g1_run)
- g2 = greenlet.greenlet(g2_run)
-
- x = g1.switch()
- self.assertEqual(x, 42)
- tpt_callback() # ensure not in the trace
- self.assertEqual(tracer.actions, [
- ('return', '__enter__'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('c_call', 'g1_run'),
- ('call', 'g2_run'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('call', '__exit__'),
- ('c_call', '__exit__'),
- ])
-
- @unittest.skipIf(*DEBUG_BUILD_PY312)
- def test_trace_events_multiple_greenlets_switching_siblings(self):
- # Like the first version, but get both greenlets running first
- # as "siblings" and then establish the tracing.
- tracer = PythonTracer()
-
- g1 = None
- g2 = None
-
- def g1_run():
- greenlet.getcurrent().parent.switch()
- tracer.__enter__()
- tpt_callback()
- g2.switch()
- tpt_callback()
- return 42
-
- def g2_run():
- greenlet.getcurrent().parent.switch()
-
- tpt_callback()
- tracer.__exit__()
- tpt_callback()
- g1.switch()
-
- g1 = greenlet.greenlet(g1_run)
- g2 = greenlet.greenlet(g2_run)
-
- # Start g1
- g1.switch()
- # And it immediately returns control to us.
- # Start g2
- g2.switch()
- # Which also returns. Now kick of the real part of the
- # test.
- x = g1.switch()
- self.assertEqual(x, 42)
-
- tpt_callback() # ensure not in the trace
- self.assertEqual(tracer.actions, [
- ('return', '__enter__'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('c_call', 'g1_run'),
- ('call', 'tpt_callback'),
- ('return', 'tpt_callback'),
- ('call', '__exit__'),
- ('c_call', '__exit__'),
- ])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_version.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_version.py
deleted file mode 100644
index 96c17cf..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_version.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#! /usr/bin/env python
-from __future__ import absolute_import
-from __future__ import print_function
-
-import sys
-import os
-from unittest import TestCase as NonLeakingTestCase
-
-import greenlet
-
-# No reason to run this multiple times under leakchecks,
-# it doesn't do anything.
-class VersionTests(NonLeakingTestCase):
- def test_version(self):
- def find_dominating_file(name):
- if os.path.exists(name):
- return name
-
- tried = []
- here = os.path.abspath(os.path.dirname(__file__))
- for i in range(10):
- up = ['..'] * i
- path = [here] + up + [name]
- fname = os.path.join(*path)
- fname = os.path.abspath(fname)
- tried.append(fname)
- if os.path.exists(fname):
- return fname
- raise AssertionError("Could not find file " + name + "; checked " + str(tried))
-
- try:
- setup_py = find_dominating_file('setup.py')
- except AssertionError as e:
- self.skipTest("Unable to find setup.py; must be out of tree. " + str(e))
-
-
- invoke_setup = "%s %s --version" % (sys.executable, setup_py)
- with os.popen(invoke_setup) as f:
- sversion = f.read().strip()
-
- self.assertEqual(sversion, greenlet.__version__)
diff --git a/venv/lib/python3.11/site-packages/greenlet/tests/test_weakref.py b/venv/lib/python3.11/site-packages/greenlet/tests/test_weakref.py
deleted file mode 100644
index 05a38a7..0000000
--- a/venv/lib/python3.11/site-packages/greenlet/tests/test_weakref.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import gc
-import weakref
-
-
-import greenlet
-from . import TestCase
-
-class WeakRefTests(TestCase):
- def test_dead_weakref(self):
- def _dead_greenlet():
- g = greenlet.greenlet(lambda: None)
- g.switch()
- return g
- o = weakref.ref(_dead_greenlet())
- gc.collect()
- self.assertEqual(o(), None)
-
- def test_inactive_weakref(self):
- o = weakref.ref(greenlet.greenlet())
- gc.collect()
- self.assertEqual(o(), None)
-
- def test_dealloc_weakref(self):
- seen = []
- def worker():
- try:
- greenlet.getcurrent().parent.switch()
- finally:
- seen.append(g())
- g = greenlet.greenlet(worker)
- g.switch()
- g2 = greenlet.greenlet(lambda: None, g)
- g = weakref.ref(g2)
- g2 = None
- self.assertEqual(seen, [None])