pax_global_header00006660000000000000000000000064141402411700014504gustar00rootroot0000000000000052 comment=c78219f22078c3ec071eb2841e3b32be6335ffa2 python-greenlet-1.1.2/000077500000000000000000000000001414024117000146315ustar00rootroot00000000000000python-greenlet-1.1.2/.clang-format000066400000000000000000000012711414024117000172050ustar00rootroot00000000000000# A clang-format style that approximates Python's PEP 7 -*- mode: yaml; -*- # Initially based on # https://gist.github.com/pganssle/0e3a5f828b4d07d79447f6ced8e7e4db BasedOnStyle: Google Language: Cpp AlignAfterOpenBracket: Align AllowShortBlocksOnASingleLine: false AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: All BinPackArguments: false BreakBeforeBraces: Stroustrup BreakBeforeTernaryOperators: true ColumnLimit: 79 DerivePointerAlignment: false IndentWidth: 4 IndentPPDirectives: AfterHash PointerAlignment: Left ReflowComments: true SpaceBeforeParens: ControlStatements SpacesInParentheses: false TabWidth: 4 UseTab: Never python-greenlet-1.1.2/.github/000077500000000000000000000000001414024117000161715ustar00rootroot00000000000000python-greenlet-1.1.2/.github/workflows/000077500000000000000000000000001414024117000202265ustar00rootroot00000000000000python-greenlet-1.1.2/.github/workflows/tests.yml000066400000000000000000000110141414024117000221100ustar00rootroot00000000000000name: tests on: [push, pull_request] env: PYTHONHASHSEED: 1042466059 ZOPE_INTERFACE_STRICT_IRO: 1 PYTHONUNBUFFERED: 1 PYTHONDONTWRITEBYTECODE: 1 PYTHONDEVMODE: 1 PIP_UPGRADE_STRATEGY: eager # Don't get warnings about Python 2 support being deprecated. We # know. The env var works for pip 20. PIP_NO_PYTHON_VERSION_WARNING: 1 PIP_NO_WARN_SCRIPT_LOCATION: 1 # Uploading built wheels for releases. # TWINE_PASSWORD is encrypted and stored directly in the # repo settings. TWINE_USERNAME: __token__ jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10.0-rc.1] os: [ubuntu-latest, macos-latest] steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Pip cache uses: actions/cache@v2 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('setup.*') }} restore-keys: | ${{ runner.os }}-pip- - name: Install dependencies run: | python -m pip install -U pip setuptools wheel python -m pip install -U twine - name: Install greenlet run: | python setup.py bdist_wheel python -m pip install -U -e ".[test,docs]" - name: Check greenlet build run: | ls -l dist twine check dist/* - name: Store greenlet wheel uses: actions/upload-artifact@v2 with: name: greenlet-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/*whl - name: Test run: | python -m unittest discover -v greenlet.tests - name: Doctest # FIXME: This conditional can go away when a Sphinx greater than 4.1.2 # is released. See https://github.com/sphinx-doc/sphinx/issues/9512 if: matrix.python-version != '3.10.0-rc.1' run: | sphinx-build -b doctest -d docs/_build/doctrees2 docs docs/_build/doctest2 - name: Publish package to PyPI (mac) # We cannot 'uses: pypa/gh-action-pypi-publish@v1.4.1' because # that's apparently a container action, and those don't run on # the Mac. if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && startsWith(runner.os, 'Mac') env: TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} run: | twine upload --skip-existing dist/* manylinux: runs-on: ubuntu-latest # We use a regular Python matrix entry to share as much code as possible. strategy: matrix: python-version: [3.9] image: [manylinux2010_x86_64, manylinux2014_aarch64, manylinux2014_ppc64le, manylinux2014_x86_64] steps: - name: checkout uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Enable emulation run: | docker run --rm --privileged hypriot/qemu-register # This one was seen in pyca/bcrypt. What's the difference? # (Other than this one not working.) #run: | # docker run --rm --privileged multiarch/qemu-user-static:register --reset - name: Build and test greenlet if: matrix.image == 'manylinux2010_x86_64' # An alternate way to do this is to run the container directly with a uses: # and then the script runs inside it. That may work better with caching. # See https://github.com/pyca/bcrypt/blob/f6b5ee2eda76d077c531362ac65e16f045cf1f29/.github/workflows/wheel-builder.yml # The 2010 image is the last one that comes with Python 2.7, # and only up through the tag 2021-02-06-3d322a5 env: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }}:2021-02-06-3d322a5 run: bash ./make-manylinux - name: Build and test greenlet (other) if: matrix.image != 'manylinux2010_x86_64' env: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} run: bash ./make-manylinux - name: Store greenlet wheels uses: actions/upload-artifact@v2 with: path: wheelhouse/*whl name: ${{ matrix.image }}_wheels.zip - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.4.1 if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') with: user: __token__ password: ${{ secrets.TWINE_PASSWORD }} skip_existing: true packages_dir: wheelhouse/ python-greenlet-1.1.2/.gitignore000066400000000000000000000001741414024117000166230ustar00rootroot00000000000000*.so *.pyd *.pyc *.pyo build/ dist/ .tox/ wheelhouse/ greenlet.egg-info/ /docs/_build __pycache__/ /.ropeproject/ /MANIFEST python-greenlet-1.1.2/.readthedocs.yml000066400000000000000000000006671414024117000177300ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally set the version of Python and requirements required to build your docs python: version: 3 install: - method: pip path: . extra_requirements: - docs python-greenlet-1.1.2/AUTHORS000066400000000000000000000015211414024117000157000ustar00rootroot00000000000000Original Authors ---------------- * Armin Rigo * Christian Tismer Contributors ------------ * Al Stone * Alexander Schmidt * Alexey Borzenkov * Andreas Schwab * Armin Ronacher * Bin Wang * Bob Ippolito * ChangBo Guo * Christoph Gohlke * Denis Bilenko * Dirk Mueller * Donovan Preston * Fantix King * Floris Bruynooghe * Fredrik Fornwall * Gerd Woetzel * Giel van Schijndel * Gökhan Karabulut * Gustavo Niemeyer * Guy Rozendorn * Hye-Shik Chang * Jared Kuolt * Jason Madden * Josh Snyder * Kyle Ambroff * Laszlo Boszormenyi * Mao Han * Marc Abramowitz * Marc Schlaich * Marcin Bachry * Matt Madison * Matt Turner * Michael Ellerman * Michael Matz * Ralf Schmitt * Robie Basak * Ronny Pfannschmidt * Samual M. Rushing * Tony Bowles * Tony Breeds * Trevor Bowen * Tulio Magno Quites Machado Filho * Ulrich Weigand * Victor Stinner python-greenlet-1.1.2/CHANGES.rst000066400000000000000000000227601414024117000164420ustar00rootroot00000000000000========= Changes ========= 1.1.2 (2021-09-29) ================== - Fix a potential crash due to a reference counting error when Python subclasses of ``greenlet.greenlet`` were deallocated. The crash became more common on Python 3.10; on earlier versions, silent memory corruption could result. See `issue 245 `_. Patch by fygao-wish. - Fix a leak of a list object when the last reference to a greenlet was deleted from some other thread than the one to which it belonged. For this to work correctly, you must call a greenlet API like ``getcurrent()`` before the thread owning the greenlet exits: this is a long-standing limitation that can also lead to the leak of a thread's main greenlet if not called; we hope to lift this limitation. Note that in some cases this may also fix leaks of greenlet objects themselves. See `issue 251 `_. - Python 3.10: Tracing or profiling into a spawned greenlet didn't work as expected. See `issue 256 `_, reported by Joe Rickerby. 1.1.1 (2021-08-06) ================== - Provide Windows binary wheels for Python 3.10 (64-bit only). - Update Python 3.10 wheels to be built against 3.10rc1, where applicable. 1.1.0 (2021-05-06) ================== - Add support for Python 3.10. Pre-built binary wheels for 3.10 are not currently available for all platforms. The greenlet ABI is different on Python 3.10 from all previous versions, but as 3.10 was never supported before, and the ABI has not changed on other Python versions, this is not considered a reason to change greenlet's major version. 1.0.0 (2021-01-13) ================== - Fix %s and %r formatting of a greenlet on Python 2. Previously it would result in a Unicode string instead of a native string. See `issue 218 `_. - Move continuous integration from Travis CI to Github Actions. 1.0a1 (2020-11-20) ================== - Add the ability to set a greenlet's PEP 567 contextvars context directly, by assigning to the greenlet's ``gr_context`` attribute. This restores support for some patterns of using greenlets atop an async environment that became more challenging in 0.4.17. Thanks to Joshua Oreman, Mike bayer, and Fantix King, among others. See `PR 198 `_. - The repr of greenlet objects now includes extra information about its state. This is purely informative and the details are subject to change. See `issue 215 `_. - The ``greenlet`` module is now a package. There are no API changes, so all existing imports, including from C code, should continue to work. - (C API) The undocumented ``GREENLET_VERSION`` macro that defined a string giving the greenlet version is now deprecated and will not be updated. - (Documentation) Publish the change log to https://greenlet.readthedocs.io Supported Platforms ------------------- - Drop support for Python 2.4, 2.5, 2.6, 3.0, 3.1, 3.2 and 3.4. The project metadata now includes the ``python_requires`` data to help installation tools understand supported versions. - Add partial support for AIX ppc64 and IBM i. Thanks to Jesse Gorzinski and Kevin Adler. See `PR 197 `_. Packaging Changes ----------------- - Require setuptools to build from source. - Stop asking setuptools to build both .tar.gz and .zip sdists. PyPI has standardized on .tar.gz for all platforms. - Stop using a custom distutils command to build extensions. distutils is deprecated. - Remove the ability to use the deprecated command ``python setup.py test``. Run greenlet tests with your favorite unittest-compatible test runner, e.g., ``python -m unittest discover greenlet.tests``. See `issue 185 `_. - The directory layout and resulting sdists have changed. See `issue 184 `_. - greenlet is now always built with support for tracing and garbage collection, and, on Python 3.7 and above, support for context variables. The internal and undocumented C preprocessor macros that could be used to alter that at compile time have been removed (no combination other than the defaults was ever tested). This helps define a stable ABI. 0.4.17 (2020-09-22) =================== - Support for PEP 567 ContextVars 0.4.16 ====== - Support for DEC Alpha architecture - Support for Python 3.9 - Support for Python 3.10a0 0.4.15 ====== - Support for RISC-V architecture - Workaround a gcc bug on ppc64 0.4.14 ====== - Support for C-SKY architecture - Fixed support for ppc64 ABI - Fixed support for Python 3.7 0.4.13 ====== - Support for Python 3.7 - Support for MinGW x64 0.4.12 ====== - Stop using trashcan api 0.4.11 ====== - Fixes for aarch64 architecture 0.4.10 ====== - Added missing files to manifest - Added workaround for ppc32 on Linux - Start building binary manylinux1 wheels 0.4.9 ===== - Fixed Windows builds 0.4.8 ===== - Added support for iOS (arm32) - Added support for ppc64le 0.4.7 ===== - Added a missing workaround for ``return 0`` on mips - Restore compatibility with Python 2.5 - Fixed stack switching on sparc 0.4.6 ===== - Expose ``_stack_saved`` property on greenlet objects, it may be used to introspect the amount of memory used by a saved stack, but the API is subject to change in the future - Added a workaround for ``return 0`` compiler optimizations on all architectures - C API typo fixes 0.4.5 ===== - Fixed several bugs in greenlet C API - Fixed a bug in multi-threaded applications, which manifested itself with spurious "cannot switch to a different thread" exceptions - Fixed some crashes on arm and mips architectures 0.4.4 ===== - Fixed PyGreenlet_SetParent signature, thanks to BoonsNaibot - Fixed 64-bit Windows builds depending on wrong runtime dll 0.4.3 ===== - Better slp_switch performance on SPARC - Drop support for Python 2.3 - Fix trashcan assertions on debug builds of Python - Remove deprecated -fno-tree-dominator-opts compiler switch - Enable switch code for SunStudio on 32-bit SunOS - Support for abc abstract methods in greenlet subclasses - Support custom directories for tests - Document switch tracing support 0.4.2 ===== - Add .travis.yml - Fix 'err' may be used uninitialized in this function - Check _MSC_VER for msvc specific code - Fix slp_switch on SPARC for multi-threaded environments - Add support for m68k 0.4.1 ===== * fix segfaults when using gcc 4.8 on amd64/x86 unix * try to disable certain gcc 4.8 optimizations that make greenlet crash * Fix greenlet on aarch64 with gcc 4.8 * workaround segfault on SunOS/sun4v * Add support for Aarch64 * Add support for x32 psABI on x86_64 * Changed memory constraints for assembly macro for PPC Linux platforms. 0.4.0 ===== * Greenlet has an instance dictionary now, which means it can be used for implementing greenlet local storage, etc. However, this might introduce incompatibility if subclasses have __dict__ in their __slots__. Classes like that will fail, because greenlet already has __dict__ out of the box. * Greenlet no longer leaks memory after thread termination, as long as terminated thread has no running greenlets left at the time. * Add support for debian sparc and openbsd5-sparc64 * Add support for ppc64 linux * Don't allow greenlets to be copied with copy.copy/deepcopy * Fix arm32/thumb support * Restore greenlet's parent after kill * Add experimental greenlet tracing 0.3.4 ===== * Use plain distutils for install command, this fixes installation of the greenlet.h header. * Enhanced arm32 support * Fix support for Linux/S390 zSeries * Workaround compiler bug on RHEL 3 / CentOS 3 0.3.3 ===== * Use sphinx to build documentation and publish it on greenlet.rtfd.org * Prevent segfaults on openbsd 4/i386 * Workaround gcc-4.0 not allowing to clobber rbx * Enhance test infrastructure * Fix possible compilation problems when including greenlet.h in C++ mode * Make the greenlet module work on x64 windows * Add a test for greenlet C++ exceptions * Fix compilation on Solaris with SunStudio 0.3.2 ===== * Fix various crashes with recent gcc versions and VC90 * Try to fix stack save/restore on arm32 * Store and restore the threadstate on exceptions like pypy/stackless do * GreenletExit is now based on BaseException on Python >= 2.5 * Switch to using PyCapsule for Python 2.7 and 3.1 * Port for AIX on PowerPC * Fix the sparc/solaris header * Improved build dependencies patch from flub. * Can't pass parent=None to greenlet.greenlet() (fixes #21) * Rudimentary gc support (only non-live greenlets are garbage collected though) 0.3.1 ===== * Fix reference leak when passing keyword arguments to greenlets (mbachry) * Updated documentation. 0.3 === * Python 3 support. * New C API to expose Greenlets to C Extensions. * greenlet.switch() now accept's keyword arguments. * Fix Python crasher caused by switching to new greenlet from another thread. * Fix Python 2.6 crash on Windows when built with VS2009. (arigo) * arm32 support from stackless (Sylvain Baro) * Linux mips support (Thiemo Seufer) * MingGW GCC 4.4 support (Giovanni Bajo) * Fix for a threading bug (issue 40 in py lib) (arigo and ghazel) * Loads more unit tests, some from py lib (3 times as many as Greenlet 0.2) * Add documentation from py lib. * General code, documentation and repository cleanup (Kyle Ambroff, Jared Kuolt) python-greenlet-1.1.2/LICENSE000066400000000000000000000026321414024117000156410ustar00rootroot00000000000000The following files are derived from Stackless Python and are subject to the same license as Stackless Python: src/greenlet/slp_platformselect.h files in src/greenlet/platform/ directory See LICENSE.PSF and http://www.stackless.com/ for details. Unless otherwise noted, the files in greenlet have been released under the following MIT license: Copyright (c) Armin Rigo, Christian Tismer and contributors 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. python-greenlet-1.1.2/LICENSE.PSF000066400000000000000000000045701414024117000162730ustar00rootroot00000000000000PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-greenlet-1.1.2/MANIFEST.in000066400000000000000000000014301414024117000163650ustar00rootroot00000000000000### # Source code ### recursive-include src *.py recursive-include src *.c recursive-include src *.cpp recursive-include src *.h recursive-include src *.cmd recursive-include src *.asm recursive-include src *.obj recursive-include benchmarks * ### # Documentation ### recursive-include docs * prune docs/_build ### # Configuration and project files ### include *.yml include *.txt include *.ini include *.rst include *.cfg include *.py include *.ini include .clang-format recursive-include appveyor *.cmd recursive-include appveyor *.ps1 recursive-include appveyor *.py recursive-include .github *.yml include AUTHORS include LICENSE include LICENSE.PSF include MANIFEST.in include make-manylinux global-exclude *.pyc global-exclude *.pyd global-exclude *.so global-exclude .coverage python-greenlet-1.1.2/README.rst000066400000000000000000000042421414024117000163220ustar00rootroot00000000000000.. This file is included into docs/history.rst .. image:: https://github.com/python-greenlet/greenlet/workflows/tests/badge.svg :target: https://github.com/python-greenlet/greenlet/actions Greenlets are lightweight coroutines for in-process concurrent programming. The "greenlet" package is a spin-off of `Stackless`_, a version of CPython that supports micro-threads called "tasklets". Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on "channels". A "greenlet", on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling; coroutines, in other words. This is useful when you want to control exactly when your code runs. You can build custom scheduled micro-threads on top of greenlet; however, it seems that greenlets are useful on their own as a way to make advanced control flow structures. For example, we can recreate generators; the difference with Python's own generators is that our generators can call nested functions and the nested functions can yield values too. (Additionally, you don't need a "yield" keyword. See the example in `test_generator.py `_). Greenlets are provided as a C extension module for the regular unmodified interpreter. .. _`Stackless`: http://www.stackless.com Who is using Greenlet? ====================== There are several libraries that use Greenlet as a more flexible alternative to Python's built in coroutine support: - `Concurrence`_ - `Eventlet`_ - `Gevent`_ .. _Concurrence: http://opensource.hyves.org/concurrence/ .. _Eventlet: http://eventlet.net/ .. _Gevent: http://www.gevent.org/ Getting Greenlet ================ The easiest way to get Greenlet is to install it with pip:: pip install greenlet Source code archives and binary distributions are vailable on the python package index at https://pypi.org/project/greenlet The source code repository is hosted on github: https://github.com/python-greenlet/greenlet Documentation is available on readthedocs.org: https://greenlet.readthedocs.io python-greenlet-1.1.2/appveyor.yml000066400000000000000000000141771414024117000172330ustar00rootroot00000000000000clone_depth: 50 max_jobs: 8 shallow_clone: true build: parallel: true verbosity: minimal # The VS 2019 image doesn't have # the MSVC needed for Python 2.7. # Note that as of 2020-11-11, this does not include # a Python 3.9 build. image: Visual Studio 2015 environment: global: APPVEYOR_SAVE_CACHE_ON_ERROR: "true" # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the # /E:ON and /V:ON options are not enabled in the batch script interpreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" # Use a fixed hash seed for reproducability PYTHONHASHSEED: 8675309 PYTHONDEVMODE: 1 # Don't get warnings about Python 2 support being deprecated. We # know. PIP_NO_PYTHON_VERSION_WARNING: 1 PIP_UPGRADE_STRATEGY: eager # Enable this if debugging a resource leak. Otherwise # it slows things down. # PYTHONTRACEMALLOC: 10 # Upload settings for twine. TWINE_USERNAME: "__token__" TWINE_PASSWORD: secure: 9JKBIB2o2S18+REaEqzUrP/1g08eRdX3eEa7D/BBN5ae0XHTlrPqbvRTNJceiUa/o3r4bejFF3o0xA69ueMd+09PGKAAAfpQnwnQPInuLVkOq3mprNk4wW0GyWLFzI3WqZhYnWH7PZtmG4Kr7mNOyd6Qdi773kN3Hn7CNhvk+ik/K3zbsGerb2YYloM/KIQSJbgdEqNcIIItoqrZzg/cqiM/47Pz7ZzcGDvevD8Nx/0lVGqFVJnj8cMevwa9iDPYn7fB59Y1GEEbtBjenrWr1Q== matrix: # http://www.appveyor.com/docs/installed-software#python - PYTHON: "C:\\Python310-x64" PYTHON_VERSION: "3.10.0rc2" PYTHON_ARCH: "64" PYTHON_EXE: python APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - PYTHON: "C:\\Python39-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.9.x" PYTHON_EXE: python APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - PYTHON: "C:\\Python39" PYTHON_ARCH: "32" PYTHON_VERSION: "3.9.x" PYTHON_EXE: python APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - PYTHON: "C:\\Python27" PYTHON_ARCH: "32" PYTHON_VERSION: "2.7.x" PYTHON_EXE: python - PYTHON: "C:\\Python27-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "2.7.x" PYTHON_EXE: python - PYTHON: "C:\\Python35" PYTHON_ARCH: "32" PYTHON_VERSION: "3.5.x" PYTHON_EXE: python - PYTHON: "C:\\Python35-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.5.x" PYTHON_EXE: python - PYTHON: "C:\\Python36" PYTHON_ARCH: "32" PYTHON_VERSION: "3.6.x" PYTHON_EXE: python - PYTHON: "C:\\Python36-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.6.x" PYTHON_EXE: python - PYTHON: "C:\\Python37" PYTHON_ARCH: "32" PYTHON_VERSION: "3.7.x" PYTHON_EXE: python - PYTHON: "C:\\Python37-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.7.x" PYTHON_EXE: python - PYTHON: "C:\\Python38" PYTHON_ARCH: "32" PYTHON_VERSION: "3.8.x" PYTHON_EXE: python - PYTHON: "C:\\Python38-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.8.x" PYTHON_EXE: python cache: - "%TMP%\\py\\" - '%LOCALAPPDATA%\pip\Cache -> appveyor.yml,setup.py' install: # If there is a newer build queued for the same PR, cancel this one. # The AppVeyor 'rollout builds' option is supposed to serve the same # purpose but it is problematic because it tends to cancel builds pushed # directly to master instead of just PR builds (or the converse). # credits: JuliaLang developers. - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } ## Debugging # - ECHO "Filesystem root:" # - ps: "ls \"C:/\"" # - ECHO "Installed SDKs:" # - ps: "if(Test-Path(\"C:/Program Files/Microsoft SDKs/Windows\")) {ls \"C:/Program Files/Microsoft SDKs/Windows\";}" # Install Python (from the official .msi of http://python.org) and pip when # not already installed. # PyPy portion based on https://github.com/wbond/asn1crypto/blob/master/appveyor.yml - ps: $env:PYTMP = "${env:TMP}\py"; if (!(Test-Path "$env:PYTMP")) { New-Item -ItemType directory -Path "$env:PYTMP" | Out-Null; } if ("${env:PYTHON_ID}" -eq "pypy") { if (!(Test-Path "${env:PYTMP}\pypy2-v7.3.1-win32.zip")) { (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.1-win32.zip', "${env:PYTMP}\pypy2-v7.3.1-win32.zip"); } 7z x -y "${env:PYTMP}\pypy2-v7.3.1-win32.zip" -oC:\ | Out-Null; } elseif (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1; } # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PYTHON%\\bin;%PATH%" - "SET PYEXE=%PYTHON%\\%PYTHON_EXE%.exe" - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Upgrade to the latest version of pip to avoid it displaying warnings # about it being out of date. Do this here instead of above in # powershell because the annoying 'DEPRECATION:blahblahblah 2.7 blahblahblah' # breaks powershell. - "%CMD_IN_ENV% %PYEXE% -mensurepip -U --user" - "%CMD_IN_ENV% %PYEXE% -mpip install -U --user pip" # Install requirements for running tests and building artifacts - "%CMD_IN_ENV% pip install --upgrade -r dev-requirements.txt" build_script: - "%CMD_IN_ENV% python -m pip install -U -e .[test,docs]" test_script: - "%CMD_IN_ENV% python -m unittest discover -v greenlet.tests" # XXX: Doctest disabled pending sphinx release for 3.10; see tests.yml. # - "%CMD_IN_ENV% python -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest" after_test: - "%CMD_IN_ENV% python setup.py bdist_wheel" artifacts: - path: dist\* deploy_script: - ps: if ($env:APPVEYOR_REPO_TAG -eq $TRUE) { pip install twine; twine upload --skip-existing dist/* } deploy: on python-greenlet-1.1.2/appveyor/000077500000000000000000000000001414024117000164765ustar00rootroot00000000000000python-greenlet-1.1.2/appveyor/install.ps1000066400000000000000000000160331414024117000205740ustar00rootroot00000000000000# Sample script to install Python and pip under Windows # Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer # License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ $MINICONDA_URL = "http://repo.continuum.io/miniconda/" $BASE_URL = "https://www.python.org/ftp/python/" $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" $GET_PIP_PATH = "C:\get-pip.py" $PYTHON_PRERELEASE_REGEX = @" (?x) (?\d+) \. (?\d+) \. (?\d+) (?[a-z]{1,2}\d+) "@ function Download ($filename, $url) { $webclient = New-Object System.Net.WebClient $basedir = $pwd.Path + "\" $filepath = $basedir + $filename if (Test-Path $filename) { Write-Host "Reusing" $filepath return $filepath } # Download and retry up to 3 times in case of network transient errors. Write-Host "Downloading" $filename "from" $url $retry_attempts = 2 for ($i = 0; $i -lt $retry_attempts; $i++) { try { $webclient.DownloadFile($url, $filepath) break } Catch [Exception]{ Start-Sleep 1 } } if (Test-Path $filepath) { Write-Host "File saved at" $filepath } else { # Retry once to get the error message if any at the last try $webclient.DownloadFile($url, $filepath) } return $filepath } function ParsePythonVersion ($python_version) { if ($python_version -match $PYTHON_PRERELEASE_REGEX) { return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, $matches.prerelease) } $version_obj = [version]$python_version return ($version_obj.major, $version_obj.minor, $version_obj.build, "") } function DownloadPython ($python_version, $platform_suffix) { $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version if (($major -le 2 -and $micro -eq 0) ` -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` ) { $dir = "$major.$minor" $python_version = "$major.$minor$prerelease" } else { $dir = "$major.$minor.$micro" } if ($prerelease) { if (($major -le 2) ` -or ($major -eq 3 -and $minor -eq 1) ` -or ($major -eq 3 -and $minor -eq 2) ` -or ($major -eq 3 -and $minor -eq 3) ` ) { $dir = "$dir/prev" } } if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { $ext = "msi" if ($platform_suffix) { $platform_suffix = ".$platform_suffix" } } else { $ext = "exe" if ($platform_suffix) { $platform_suffix = "-$platform_suffix" } } $filename = "python-$python_version$platform_suffix.$ext" $url = "$BASE_URL$dir/$filename" $filepath = Download $filename $url return $filepath } function InstallPython ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "" } else { $platform_suffix = "amd64" } $installer_path = DownloadPython $python_version $platform_suffix $installer_ext = [System.IO.Path]::GetExtension($installer_path) Write-Host "Installing $installer_path to $python_home" $install_log = $python_home + ".log" if ($installer_ext -eq '.msi') { InstallPythonMSI $installer_path $python_home $install_log } else { InstallPythonEXE $installer_path $python_home $install_log } if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallPythonEXE ($exepath, $python_home, $install_log) { $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" RunCommand $exepath $install_args } function InstallPythonMSI ($msipath, $python_home, $install_log) { $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" $uninstall_args = "/qn /x $msipath" RunCommand "msiexec.exe" $install_args if (-not(Test-Path $python_home)) { Write-Host "Python seems to be installed else-where, reinstalling." RunCommand "msiexec.exe" $uninstall_args RunCommand "msiexec.exe" $install_args } } function RunCommand ($command, $command_args) { Write-Host $command $command_args Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru } function InstallPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $python_path = $python_home + "\python.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $webclient = New-Object System.Net.WebClient $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) Write-Host "Executing:" $python_path $GET_PIP_PATH & $python_path $GET_PIP_PATH } else { Write-Host "pip already installed." } } function DownloadMiniconda ($python_version, $platform_suffix) { if ($python_version -eq "3.4") { $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" } else { $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" } $url = $MINICONDA_URL + $filename $filepath = Download $filename $url return $filepath } function InstallMiniconda ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "x86" } else { $platform_suffix = "x86_64" } $filepath = DownloadMiniconda $python_version $platform_suffix Write-Host "Installing" $filepath "to" $python_home $install_log = $python_home + ".log" $args = "/S /D=$python_home" Write-Host $filepath $args Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallMinicondaPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $conda_path = $python_home + "\Scripts\conda.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $args = "install --yes pip" Write-Host $conda_path $args Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru } else { Write-Host "pip already installed." } } function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON InstallPip $env:PYTHON } main python-greenlet-1.1.2/appveyor/run_with_env.cmd000066400000000000000000000064461414024117000217040ustar00rootroot00000000000000:: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific :: environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ :: :: Notes about batch files for Python people: :: :: Quotes in values are literally part of the values: :: SET FOO="bar" :: FOO is now five characters long: " b a r " :: If you don't want quotes, don't include them on the right-hand side. :: :: The CALL lines at the end of this file look redundant, but if you move them :: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y :: case, I don't know why. @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf :: Extract the major and minor versions, and allow for the minor version to be :: more than 9. This requires the version number to have two dots in it. SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% IF "%PYTHON_VERSION:~3,1%" == "." ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% ) ELSE ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% ) :: Based on the Python version, determine what SDK version to use, and whether :: to set the SDK for 64-bit. IF %MAJOR_PYTHON_VERSION% == 2 ( SET WINDOWS_SDK_VERSION="v7.0" SET SET_SDK_64=Y ) ELSE ( IF %MAJOR_PYTHON_VERSION% == 3 ( SET WINDOWS_SDK_VERSION="v7.1" IF %MINOR_PYTHON_VERSION% LEQ 4 ( SET SET_SDK_64=Y ) ELSE ( SET SET_SDK_64=N IF EXIST "%WIN_WDK%" ( :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN "%WIN_WDK%" 0wdf ) ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) ) IF %PYTHON_ARCH% == 64 ( IF %SET_SDK_64% == Y ( ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ELSE ( ECHO Using default MSVC build environment for 64 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) python-greenlet-1.1.2/benchmarks/000077500000000000000000000000001414024117000167465ustar00rootroot00000000000000python-greenlet-1.1.2/benchmarks/chain.py000077500000000000000000000020201414024117000203770ustar00rootroot00000000000000#!/usr/bin/env python """Create a chain of coroutines and pass a value from one end to the other, where each coroutine will increment the value before passing it along. """ import optparse import time import greenlet def link(next_greenlet): value = greenlet.getcurrent().parent.switch() next_greenlet.switch(value + 1) def chain(n): start_node = greenlet.getcurrent() for i in xrange(n): g = greenlet.greenlet(link) g.switch(start_node) start_node = g return start_node.switch(0) if __name__ == '__main__': p = optparse.OptionParser( usage='%prog [-n NUM_COROUTINES]', description=__doc__) p.add_option( '-n', type='int', dest='num_greenlets', default=100000, help='The number of greenlets in the chain.') options, args = p.parse_args() if len(args) != 0: p.error('unexpected arguments: %s' % ', '.join(args)) start_time = time.clock() print 'Result:', chain(options.num_greenlets) print time.clock() - start_time, 'seconds' python-greenlet-1.1.2/dev-requirements.txt000066400000000000000000000000211414024117000206620ustar00rootroot00000000000000setuptools wheel python-greenlet-1.1.2/docs/000077500000000000000000000000001414024117000155615ustar00rootroot00000000000000python-greenlet-1.1.2/docs/Makefile000066400000000000000000000127041414024117000172250ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/greenlet.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/greenlet.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/greenlet" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/greenlet" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." python-greenlet-1.1.2/docs/api.rst000066400000000000000000000046051414024117000170710ustar00rootroot00000000000000====================== Python API Reference ====================== .. currentmodule:: greenlet Exceptions ========== .. autoexception:: GreenletExit .. autoexception:: error Greenlets ========= .. autofunction:: getcurrent .. autoclass:: greenlet Greenlets support boolean tests: ``bool(g)`` is true if ``g`` is active and false if it is dead or not yet started. .. method:: switch(*args, **kwargs) Switches execution to this greenlet. See :ref:`switching`. .. automethod:: throw .. autoattribute:: dead True if this greenlet is dead (i.e., it finished its execution). .. autoattribute:: gr_context The :class:`contextvars.Context` in which ``g`` will run. Writable; defaults to ``None``, reflecting that a greenlet starts execution in an empty context unless told otherwise. Generally, this should only be set once, before a greenlet begins running. Accessing or modifying this attribute raises :exc:`AttributeError` on Python versions 3.6 and earlier (which don't natively support the `contextvars` module) or if ``greenlet`` was built without contextvars support. For more information, see :doc:`contextvars`. .. versionadded:: 1.0.0 .. autoattribute:: gr_frame The frame that was active in this greenlet when it most recently called ``some_other_greenlet.switch()``, and that will resume execution when ``this_greenlet.switch()`` is next called. The remainder of the greenlet's stack can be accessed by following the frame object's ``f_back`` attributes. ``gr_frame`` is non-None only for suspended greenlets; it is None if the greenlet is dead, not yet started, or currently executing. .. autoattribute:: parent The parent greenlet. This is writable, but it is not allowed to create cycles of parents. A greenlet without a parent is the main greenlet of its thread. Cannot be set to anything except a greenlet. .. autoattribute:: run The callable that this greenlet will run when it starts. After it is started, this attribute no longer exists. Subclasses can define this as a method on the type. Tracing ======= For details on tracing, see :doc:`tracing`. .. autofunction:: gettrace .. autofunction:: settrace :param callback: A callable object with the signature ``callback(event, args)``. python-greenlet-1.1.2/docs/c_api.rst000066400000000000000000000061661414024117000173770ustar00rootroot00000000000000================= C API Reference ================= Greenlets can be created and manipulated from extension modules written in C or C++, or from applications that embed Python. The ``greenlet.h`` header is provided, and exposes the entire API available to pure Python modules. Note that much of the API is implemented in terms of macros, meaning that it is not necessarily ABI stable. Types ===== .. c:type:: PyGreenlet The C name corresponding to the Python :class:`greenlet.greenlet`. Exceptions ========== .. c:type:: PyExc_GreenletError The C name corresponding to the Python :exc:`greenlet.error` .. c:type:: PyExc_GreenletExit The C name corresponding to the Python :exc:`greenlet.GreenletExit` Functions ========= .. c:function:: void PyGreenlet_Import(void) A macro that imports the greenlet module and initializes the C API. This must be called once for each extension module that uses the greenlet C API, usually in the module's init function. .. c:function:: int PyGreenlet_Check(PyObject* p) Macro that returns true if the argument is a :c:type:`PyGreenlet`. .. c:function:: int PyGreenlet_STARTED(PyGreenlet* g) Macro that returns true if the greenlet *g* has started. .. c:function:: int PyGreenlet_ACTIVE(PyGreenlet* g) Macro that returns true if the greenlet *g* has started and has not died. .. c:function:: PyGreenlet* PyGreenlet_GET_PARENT(PyGreenlet* g) Macro that returns the parent greenlet of *g*. .. c:function:: int PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent) Set the parent greenlet of *g*. :return: 0 for succes, or -1 on error. When an error is returned, *g* is not a pointer to a greenlet, and an :exc:`AttributeError` has been raised. .. c:function:: PyGreenlet* PyGreenlet_GetCurrent(void) Returns the currently active greenlet object. .. c:function:: PyGreenlet* PyGreenlet_New(PyObject* run, PyObject* parent) Creates a new greenlet object with the callable *run* and parent *parent*. Both parameters are optional and may be ``NULL``. :param run: If ``NULL``, the greenlet will be created, but will fail when switched to. :param parent: If ``NULL``, the parent is automatically set to the current greenlet. .. c:function:: PyObject* PyGreenlet_Switch(PyGreenlet* g, PyObject* args, PyObject* kwargs) Switches to the greenlet *g*. Besides *g*, the remaining parameters are optional and may be ``NULL``. :param args: If ``args`` is NULL, an empty tuple is passed to the target greenlet. If given, must be a :class:`tuple`. :param kwargs: If kwargs is ``NULL``, no keyword arguments are passed to the target greenlet. If given, must be a :class:`dict`. .. c:function:: PyObject* PyGreenlet_Throw(PyGreenlet* g, PyObject* typ, PyObject* val, PyObject* tb) Switches to greenlet *g*, but immediately raise an exception of type *typ* with the value *val*, and optionally, the traceback object *tb*. *tb* can be ``NULL``. The arguments *typ*, *val* and *tb* are interpreted as for :c:func:`PyErr_Restore`. python-greenlet-1.1.2/docs/caveats.rst000066400000000000000000000021611414024117000177410ustar00rootroot00000000000000========================== Caveats and Known Issues ========================== This document will describe known issues and sharp edges of greenlets. Native Functions Should Be Re-entrant ===================================== Use caution when switching greenlet stacks that include native (C) frames. Much like with threads, if the library function is not re-entrant, and more than one greenlet attempts to enter it, subtle problems can result. Common constructs in C that may not be reentrant include: - static variables in functions; - global variables. This was the source of an issue in gevent that led to corruption of libuv's internal state. The fix was to avoid re-entering the vulnerable function. Use Caution Mixing Greenlets and Signal Handlers ================================================ In CPython, signal handler functions *must* return in order for the rest of the program to proceed. Switching greenlets in a signal handler to, for example, get back to the main greenlet, such that the signal handler function doesn't really return to CPython, is likely to lead to a hang. See :issue:`143` for an example. python-greenlet-1.1.2/docs/changes.rst000066400000000000000000000000341414024117000177200ustar00rootroot00000000000000.. include:: ../CHANGES.rst python-greenlet-1.1.2/docs/conf.py000066400000000000000000000212771414024117000170710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # greenlet documentation build configuration file, created by # sphinx-quickstart on Tue Dec 27 22:58:53 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) import pkg_resources sys.path.append(os.path.abspath('../src/')) rqmt = pkg_resources.require('greenlet')[0] # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = [] # ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'greenlet' copyright = u'2011, Armin Rigo, Christian Tismer' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%s.%s' % tuple(rqmt.version.split('.')[:2]) # The full version, including alpha/beta/rc tags. release = rqmt.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = "obj" # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'greenletdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'greenlet.tex', u'greenlet Documentation', u'Armin Rigo, Christian Tismer', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'greenlet', u'greenlet Documentation', [u'Armin Rigo, Christian Tismer'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'greenlet', u'greenlet Documentation', u'Armin Rigo, Christian Tismer', 'greenlet', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' intersphinx_mapping = { 'https://docs.python.org/': None, 'https://www.gevent.org/': None, } # Sphinx 1.8+ prefers this to `autodoc_default_flags`. It's documented that # either True or None mean the same thing as just setting the flag, but # only None works in 1.8 (True works in 2.0) autodoc_default_options = { # Our only type with members is ``greenlet``, and it # currently contains malformed ReST #'members': None, 'show-inheritance': None, } autodoc_member_order = 'bysource' autoclass_content = 'both' extlinks = { 'issue': ('https://github.com/python-greenlet/greenlet/issues/%s', 'issue #'), 'pr': ('https://github.com/python-greenlet/greenlet/pull/%s', 'pull request #') } python-greenlet-1.1.2/docs/contextvars.rst000066400000000000000000000114141414024117000206740ustar00rootroot00000000000000============================= Context Variables (asyncio) ============================= .. versionadded:: 1.0.0 On Python versions (3.7 and above) that natively support context variables as defined in :pep:`567`, each greenlet runs by default in its own :class:`contextvars.Context`, enabling :class:`~contextvars.ContextVar`\s to be used for "greenlet-local storage". (If you need to support earlier Python versions, you can use attributes on the greenlet object instead.) A new greenlet's context is initially empty, i.e., all :class:`~contextvars.ContextVar`\s have their default values. This matches the behavior of a new thread, but differs from that of a new :class:`asyncio.Task`, which inherits a copy of the context that was active when it was spawned. You can assign to a greenlet's ``gr_context`` attribute to change the context that it will use. For example: .. doctest:: :pyversion: > 3.7 >>> import greenlet >>> import contextvars >>> example = contextvars.ContextVar("example", default=0) >>> def set_it(next_value): ... previous_value = example.get() ... print("Value of example in greenlet :", previous_value) ... print("Setting example in greenlet to:", next_value) ... example.set(next_value) >>> _ = example.set(1) By default, a new greenlet gets an empty context, unrelated to the current context: .. doctest:: :pyversion: > 3.7 >>> gr1 = greenlet.greenlet(set_it) >>> gr1.switch(2) Value of example in greenlet : 0 Setting example in greenlet to: 2 >>> example.get() 1 You can make a greenlet get a copy of the current context when it is created, like asyncio: .. doctest:: :pyversion: > 3.7 >>> gr2 = greenlet.greenlet(set_it) >>> gr2.gr_context = contextvars.copy_context() >>> gr2.switch(2) Value of example in greenlet : 1 Setting example in greenlet to: 2 You can also make a greenlet *share* the current context, like older, non-contextvars-aware versions of greenlet: .. doctest:: :pyversion: > 3.7 >>> gr3 = greenlet.greenlet(set_it) >>> gr3.gr_context = greenlet.getcurrent().gr_context >>> gr3.switch(2) Value of example in greenlet : 1 Setting example in greenlet to: 2 You can alternatively set a new greenlet's context by surrounding its top-level function in a call to :meth:`Context.run() `: .. doctest:: :pyversion: > 3.7 >>> _ = example.set(1) >>> gr4 = greenlet.greenlet(contextvars.copy_context().run) >>> gr4.switch(set_it, 2) Value of example in greenlet : 1 Setting example in greenlet to: 2 >>> example.get() 1 However, contextvars were not designed with greenlets in mind, so using :meth:`Context.run() ` becomes challenging in an environment with arbitrary greenlet-to-greenlet control transfers. The :meth:`~contextvars.Context.run` calls across all greenlets in a thread must effectively form a stack, where the last context entered is the first one to be exited. Also, it's not possible to have two calls to :meth:`~contextvars.Context.run` for the same context active in two different greenlets at the same time. Assigning to ``gr_context`` does not share these restrictions. You can access and change a greenlet's context almost no matter what state the greenlet is in. It can be dead, not yet started, or suspended (on any thread), or running (on the current thread only). Accessing or modifying ``gr_context`` of a greenlet running on a different thread raises :exc:`ValueError`. .. warning:: Changing the ``gr_context`` after a greenlet has begun running is not recommended for reasons outlined below. Once a greenlet has started running, ``gr_context`` tracks its *current* context: the one that would be active if you switched to the greenlet right now. This may not be the same as the value of ``gr_context`` before the greenlet started running. One potential difference occurs if a greenlet running in the default empty context (represented as ``None``) sets any context variables: a new :class:`~contextvars.Context` will be implicitly created to hold them, which will be reflected in ``gr_context``. Another one occurs if a greenlet makes a call to ``Context.run(some_inner, func)``: its ``gr_context`` will be ``some_inner`` until ``func()`` returns. .. warning:: Assigning to ``gr_context`` of an active greenlet that might be inside a call to :meth:`Context.run() ` is not recommended, because :meth:`~contextvars.Context.run` will raise an exception if the current context when it exits doesn't match the context that it set upon entry. The safest thing to do is set ``gr_context`` once, before starting the greenlet; then there's no potential conflict with :meth:`Context.run() ` calls. python-greenlet-1.1.2/docs/creating_executing_greenlets.rst000066400000000000000000000112161414024117000242330ustar00rootroot00000000000000================================== Creating And Executing Greenlets ================================== .. This document is a mess. It's a cross between how-to and API reference. .. currentmodule:: greenlet To create a new greenlet, simply instantiate a new object of class :class:`greenlet.greenlet`, passing it the initial function to run. .. tip:: If you're using a framework built on greenlets, such as :mod:`gevent`, consult its documentation. Some frameworks have other ways of creating new greenlets (for example, :func:`gevent.spawn`) or prefer a different greenlet class (for example, :class:`gevent.Greenlet`). .. doctest:: >>> import greenlet >>> def run(): ... print("Running in the greenlet function.") >>> glet = greenlet.greenlet(run) The greenlet will have its ``run`` attribute set to the function you passed, and its :ref:`parent ` will be the :func:`current greenlet `. .. doctest:: >>> glet.run is run True >>> glet.parent is greenlet.getcurrent() True Execution of the greenlet begins when :meth:`greenlet.switch` is called on it. .. doctest:: >>> glet.switch() Running in the greenlet function. The ``run`` attribute is deleted at that time. .. doctest:: >>> glet.run Traceback (most recent call last): ... AttributeError: run .. _subclassing_greenlet: Subclassing greenlet ==================== You can also subclass :class:`greenlet.greenlet` and define ``run`` as a method. This is useful to store additional state with the greenlet. .. doctest:: >>> import time >>> class MyGreenlet(greenlet.greenlet): ... created_at = None ... finished_at = None ... def run(self): ... self.created_at = time.time() ... print("Running in the greenlet subclass.") ... self.finished_at = time.time() >>> glet = MyGreenlet() >>> glet.switch() Running in the greenlet subclass. >>> glet.finished_at >= glet.created_at True See :ref:`switching` for more information about switching into greenlets. .. _changing_the_parent: Changing The Parent =================== When a greenlet finishes, :ref:`execution resumes with its parent `. This defaults to the current greenlet when the object was instantiated, but can be changed either at that time or any time later. To set it at creation time, pass the desired parent as the second argument: .. doctest:: >>> def parent(child_result): ... print("In the parent.") >>> parent_glet = greenlet.greenlet(parent) >>> def child(): ... print("In the child.") >>> child_glet = greenlet.greenlet(child, parent_glet) >>> child_glet.switch() In the child. In the parent. To change it later, assign to the ``greenlet.parent`` attribute. .. doctest:: >>> parent_glet = greenlet.greenlet(parent) >>> child_glet = greenlet.greenlet(child) >>> child_glet.parent = parent_glet >>> child_glet.switch() In the child. In the parent. Of course, cycles are not permitted. .. doctest:: >>> parent_glet = greenlet.greenlet(parent) >>> child_glet = greenlet.greenlet(child) >>> child_glet.parent = parent_glet >>> parent_glet.parent = child_glet Traceback (most recent call last): ... ValueError: cyclic parent chain The parent must be a greenlet. .. doctest:: >>> parent_glet.parent = 42 Traceback (most recent call last): ... TypeError: parent must be a greenlet Interrupting Greenlets by Throwing Exceptions ============================================= Besides simply :meth:`switching ` into a greenlet, you can also have it resume execution by throwing an exception into it. This is useful to interrupt a loop in the greenlet, for instance. .. doctest:: >>> main = greenlet.getcurrent() >>> class MyException(Exception): ... pass >>> def run(): ... try: ... main.switch() ... except MyException: ... print("Caught exception in greenlet.") >>> glet = greenlet.greenlet(run) >>> _ = glet.switch() >>> glet.throw(MyException) Caught exception in greenlet. Uncaught exceptions thrown into the greenlet will propagate into the parent greenlet. .. doctest:: >>> glet = greenlet.greenlet(run) >>> _ = glet.switch() >>> glet.throw(ValueError) Traceback (most recent call last): ... ValueError As a special case, if the uncaught exception is :exc:`greenlet.GreenletExit`, it will *not* propagate but instead be returned. This is commonly used to signal an "expected exit". .. doctest:: >>> glet = greenlet.greenlet(run) >>> _ = glet.switch() >>> glet.throw(greenlet.GreenletExit) GreenletExit() python-greenlet-1.1.2/docs/development.rst000066400000000000000000000013401414024117000206330ustar00rootroot00000000000000===================== Development Process ===================== This document is a collection of notes about how greenlet is developed. Github ====== The primary development location for greenlet is GitHub: https://github.com/python-greenlet/greenlet/ Releases ======== greenlet uses Semantic Versions; this includes changes to the ABI (breaking the ABI is considered a major change). Releases are made using `zest.releaser `_. .. code-block:: shell $ pip install zest.releaser[recommended] $ fullrelease Binary wheels are created and uploaded to PyPI for Windows, macOS, and Linux (x86_64 and aarch64) when a tag is pushed to the repository. The above command does this. python-greenlet-1.1.2/docs/greenlet.rst000066400000000000000000000133751414024117000201310ustar00rootroot00000000000000.. _what_is_greenlet: =================== greenlet Concepts =================== .. currentmodule:: greenlet .. |--| unicode:: U+2013 .. en dash .. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace :trim: A "greenlet" is a small independent pseudo-thread. Think about it as a small stack of frames; the outermost (bottom) frame is the initial function you called, and the innermost frame is the one in which the greenlet is currently paused. In code, greenlets are represented by objects of class :class:`greenlet`. These objects have a few defined attributes, and also have a ``__dict__``, allowing for arbitrary user-defined attributes. .. warning:: Attribute names beginning with ``gr_`` are reserved for this library. Switching greenlets =================== .. seealso:: :doc:`switching` You work with greenlets by creating a number of such stacks and jumping execution between them. Jumps are never implicit: a greenlet must choose to jump to another greenlet, which will cause the former to suspend and the latter to resume where it was suspended. Jumping between greenlets is called "switching". Similarly to ``generator.send(val)``, switching may pass objects between greenlets. The greenlet Lifecycle ====================== .. seealso:: Details And Examples :doc:`creating_executing_greenlets` Where does execution go when a greenlet dies? :ref:`greenlet_parents` When you create a greenlet, it gets an initially empty stack; when you first switch to it, it starts to run a specified function, which may call other functions, switch out of the greenlet, etc. When eventually the outermost function finishes its execution, the greenlet's stack becomes empty again and the greenlet is "dead". Greenlets can also die of an uncaught exception, or be :doc:`garbage collected ` (which raises an exception). .. rubric:: Example Let's quickly pull together an example demonstrating those concepts before continuing with a few more concepts. .. doctest:: >>> from greenlet import greenlet >>> def test1(): ... print("[gr1] main -> test1") ... gr2.switch() ... print("[gr1] test1 <- test2") ... return 'test1 done' >>> def test2(): ... print("[gr2] test1 -> test2") ... gr1.switch() ... print("This is never printed.") >>> gr1 = greenlet(test1) >>> gr2 = greenlet(test2) >>> gr1.switch() [gr1] main -> test1 [gr2] test1 -> test2 [gr1] test1 <- test2 'test1 done' >>> gr1.dead True >>> gr2.dead False The line ``gr1.switch()`` jumps to ``test1``, which prints that, jumps to ``test2``, and prints that, jumps back into ``test1``, prints that; and then ``test1`` finishes and ``gr1`` dies. At this point, the execution comes back to the original ``gr1.switch()`` call, which returns the value that ``test1`` returned. Note that ``test2`` is never switched back to and so doesn't print its final line; it is also not dead. Having seen that, we can continue with a few more concepts. The Current greenlet ==================== The greenlet that is actively running code is called the "current greenlet." The :class:`greenlet` object representing the current greenlet can be obtained by calling :func:`getcurrent`. (Note that :ref:`this could be a subclass `.) As long as a greenlet is running, no other greenlet can be running. Execution must be explicitly transferred by switching to a different greenlet. The Main greenlet ================= Initially, there is one greenlet that you don't have to create: the main greenlet. This is the only greenlet that can ever have :ref:`a parent of None `. The main greenlet can never be dead. This is true for :doc:`every thread in a process `. .. rubric:: Example .. doctest:: >>> from greenlet import getcurrent >>> def am_i_main(): ... current = getcurrent() ... return current.parent is None >>> am_i_main() True >>> glet = greenlet(am_i_main) >>> glet.switch() False .. _greenlet_parents: Greenlet Parents ================ Every greenlet, except the main greenlet, has a "parent" greenlet. The parent greenlet defaults to being the one in which the greenlet was created (this can be :ref:`changed at any time `). In this way, greenlets are organized in a tree. Top-level code that doesn't run in a user-created greenlet runs in the implicit main greenlet, which is the root of the tree. The parent is where execution continues when a greenlet dies, whether by explicitly returning from its function, "falling off the end" of its function, or by raising an uncaught exception. In the above example, both ``gr1`` and ``gr2`` have the main greenlet as a parent. Whenever one of them dies, the execution comes back to "main". Uncaught Exceptions are Raised In the Parent -------------------------------------------- Uncaught exceptions are propagated into the parent, too. For example, if the above ``test2()`` contained a typo, it would generate a :exc:`NameError` that would kill ``gr2``, and the exception would go back directly into "main". The traceback would show ``test2``, but not ``test1``. Remember, switches are not calls, but transfer of execution between parallel "stack containers", and the "parent" defines which stack logically comes "below" the current one. .. doctest:: >>> def test2(): ... print(this_should_be_a_name_error) >>> gr1 = greenlet(test1) >>> gr2 = greenlet(test2) >>> gr1.switch() Traceback (most recent call last): ... File "", line 1, in gr1.switch() File "", line 2, in test2 print(this_should_be_a_name_error) NameError: name 'this_should_be_a_name_error' is not defined python-greenlet-1.1.2/docs/greenlet_gc.rst000066400000000000000000000141111414024117000205670ustar00rootroot00000000000000================================== Garbage Collection and greenlets ================================== .. currentmodule:: greenlet If all the references to a greenlet object go away (including the references from the parent attribute of other greenlets), then there is no way to ever switch back to this greenlet. In this case, a :exc:`GreenletExit` exception is generated into the greenlet. This is the only case where a greenlet receives the execution asynchronously (without an explicit call to :meth:`greenlet.switch`). This gives ``try/finally`` blocks a chance to clean up resources held by the greenlet. This feature also enables a programming style in which greenlets are infinite loops waiting for data and processing it. Such loops are automatically interrupted when the last reference to the greenlet goes away. .. doctest:: >>> from greenlet import getcurrent, greenlet, GreenletExit >>> def run(): ... print("Beginning greenlet") ... try: ... while 1: ... print("Switching to parent") ... getcurrent().parent.switch() ... except GreenletExit: ... print("Got GreenletExit; quitting") >>> glet = greenlet(run) >>> _ = glet.switch() Beginning greenlet Switching to parent >>> glet = None Got GreenletExit; quitting The greenlet is expected to either die or be resurrected by having a new reference to it stored somewhere; just catching and ignoring the `GreenletExit` is likely to lead to an infinite loop. Cycles In Frames ================ Greenlets participate in garbage collection in a limited fashion; cycles involving data that is present in a greenlet's frames may not be detected. .. warning:: In particular, storing references to other greenlets cyclically may lead to leaks. .. note:: We use an object with ``__del__`` methods to demonstrate when they are collected. These examples require Python 3 to run; Python 2 won't collect cycles if the ``__del__`` method is defined. Manually Clearing Cycles Works ------------------------------ Here, we define a function that creates a cycle; when we run it and then collect garbage, the cycle is found and cleared, even while the function is running. .. important:: The examples that find and collect the cycle do so because we're manually removing the top-level references to the cycle by deleting the variables in the frame. .. doctest:: :pyversion: >= 3.5 >>> import gc >>> class Cycle(object): ... def __del__(self): ... print("(Running finalizer)") >>> def collect_it(): ... print("Collecting garbage") ... gc.collect() >>> def run(collect=collect_it): ... cycle1 = Cycle() ... cycle2 = Cycle() ... cycle1.cycle = cycle2 ... cycle2.cycle = cycle1 ... print("Deleting cycle vars") ... del cycle1 ... del cycle2 ... collect() ... print("Returning") >>> run() Deleting cycle vars Collecting garbage (Running finalizer) (Running finalizer) Returning If we use the same function in a greenlet, the cycle is also found while the greenlet is active: .. doctest:: :pyversion: >= 3.5 >>> glet = greenlet(run) >>> _ = glet.switch() Deleting cycle vars Collecting garbage (Running finalizer) (Running finalizer) Returning If we tweak the function to return control to a different greenlet (the main greenlet) and then run garbage collection, the cycle is also found: .. doctest:: :pyversion: >= 3.5 >>> glet = greenlet(run) >>> _ = glet.switch(getcurrent().switch) Deleting cycle vars >>> collect_it() Collecting garbage (Running finalizer) (Running finalizer) >>> del glet Cycles In Suspended Frames Are Not Collected ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Where this can fall apart is if a greenlet is left suspended and not switched to. Cycles within the suspended frames will not be detected; note how we don't run finalizers here when the ``outer`` greenlet runs a collection: .. doctest:: :pyversion: >= 3.5 >>> def inner(): ... cycle1 = Cycle() ... cycle2 = Cycle() ... cycle1.cycle = cycle2 ... cycle2.cycle = cycle1 ... getcurrent().parent.switch() >>> def outer(): ... glet = greenlet(inner) ... glet.switch() ... collect_it() >>> outer_glet = greenlet(outer) >>> outer_glet.switch() Collecting garbage It's only when the ``inner`` greenlet becomes garbage itself that its frames and cycles can be freed: .. doctest:: :pyversion: >= 3.5 >>> outer_glet.dead True >>> collect_it() Collecting garbage (Running finalizer) (Running finalizer) A Cycle Of Greenlets Is A Leak ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What if we introduce a cycle among the greenlets themselves while also leaving a greenlet suspended? Here, the frames of the ``inner`` greenlet refer to the ``outer`` (as the ``inner`` greenlet itself does), and both the frames of the ``outer``, as well as the ``outer`` greenlet itself, refer to the ``inner``: .. doctest:: :pyversion: >= 3.5 >>> def inner(): ... cycle1 = Cycle() ... cycle2 = Cycle() ... cycle1.cycle = cycle2 ... cycle2.cycle = cycle1 ... parent = getcurrent().parent ... parent.switch() >>> def outer(): ... glet = greenlet(inner) ... getcurrent().child_greenlet = glet ... glet.switch() ... collect_it() This time, even letting the outer and inner greenlets die doesn't find the cycle hidden in the inner greenlet's frame: .. doctest:: :pyversion: >= 3.5 >>> outer_glet = greenlet(outer) >>> outer_glet.switch() Collecting garbage >>> outer_glet.dead True >>> collect_it() Collecting garbage Even explicitly deleting the outer greenlet doesn't find and clear the cycle; we have created a legitimate memory leak, not just of the greenlet objects, but also the objects in any suspended frames: .. doctest:: :pyversion: >= 3.5 >>> del outer_glet >>> collect_it() Collecting garbage python-greenlet-1.1.2/docs/gui_example.rst000066400000000000000000000220631414024117000206150ustar00rootroot00000000000000 .. _gui_example: ================================================================== Motivation: Treating an Asynchronous GUI Like a Synchronous Loop ================================================================== .. currentmodule:: greenlet In this document, we'll demonstrate how greenlet can be used to connect synchronous and asynchronous operations, without introducing any additional threads or race conditions. We'll use the example of transforming a "pull"-based console application into an asynchronous "push"-based GUI application *while still maintaining the simple pull-based structure*. Similar techniques work with XML expat parsers; in general, it can be framework that issues asynchronous callbacks. .. |--| unicode:: U+2013 .. en dash .. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace :trim: A Simple Terminal App ===================== Let's consider a system controlled by a terminal-like console, where the user types commands. Assume that the input comes character by character. In such a system, there will typically be a loop like the following one: .. doctest:: >>> def echo_user_input(user_input): ... print(' <<< ' + user_input.strip()) ... return user_input >>> def process_commands(): ... while True: ... line = '' ... while not line.endswith('\n'): ... line += read_next_char() ... echo_user_input(line) ... if line == 'quit\n': ... print("Are you sure?") ... if echo_user_input(read_next_char()) != 'y': ... continue # ignore the command ... print("(Exiting loop.)") ... break # stop the command loop ... process_command(line) Here, we have an infinite loop. The job of the loop is to read characters that the user types, accumulate that into a command line, and then execute the command. The heart of the loop is around ``read_next_char()``: .. doctest:: >>> def read_next_char(): ... """ ... Called from `process_commands`; ... blocks until a character has been typed and returns it. ... """ This function might be implemented by simply reading from :obj:`sys.stdin`, or by something more complex such as :meth:`curses.window.getch`, but in any case, it doesn't return until a key has been read from the user. Competing Event Loops ===================== Now assume that you want to plug this program into a GUI. Most GUI toolkits are event-based. Internally, they run their own infinite loop much like the one we wrote above, invoking a call-back for each character the user presses (``event_keydown(key)``). .. doctest:: >>> def event_keydown(key): ... "Called by the event system *asynchronously*." In this setting, it is difficult to implement the ``read_next_char()`` function needed by the code above. We have two incompatible functions. First, there's the function the GUI will call asynchronously to notify about an event; it's important to stress that we're not in control of when this function is called |---| in fact, our code isn't in the call stack at all, the GUI's loop is the only thing running. But that doesn't fit with our second function, ``read_next_char()`` which itself is supposed to be blocking and called from the middle of its own loop. How can we fit this asynchronous delivery mechanism together with our synchronous, blocking function that reads the next character the user types? Enter greenlets: Dual Infinite Loops ==================================== You might consider doing that with :class:`threads ` [#f1]_, but that can get complicated rather quickly. greenlets are an alternate solution that don't have the related locking and other problems threads introduce. By introducing a greenlet to run ``process_commands``, and having it communicate with the greenlet running the GUI event loop, we can effectively have a single thread be *in the middle of two infinite loops at once* and switch between them as desired. Pretty cool. It's even cooler when you consider that the GUI's loop is likely to be implemented in C, not Python, so we'll be switching between infinite loops both in native code and in the Python interpreter. First, let's create a greenlet to run the ``process_commands`` function (note that we're not starting it just yet, only defining it). .. doctest:: >>> from greenlet import greenlet >>> g_processor = greenlet(process_commands) Now, we need to arrange for the communication between the GUI's event loop and its callback ``event_keydown`` (running in the implicit main greenlet) and this new greenlet. The changes to ``event_keydown`` are pretty simple: just send the key the GUI gives us into the loop that ``process_commands`` is in using :meth:`greenlet.switch`. .. doctest:: >>> main_greenlet = greenlet.getcurrent() >>> def event_keydown(key): # running in main_greenlet ... # jump into g_processor, sending it the key ... g_processor.switch(key) The other side of the coin is to define ``read_next_char`` to accept this key event. We do this by letting the main greenlet run the GUI loop until the GUI loop jumps back to is from ``event_keydown``: .. doctest:: >>> def read_next_char(): # running in g_processor ... # jump to the main greenlet, where the GUI event ... # loop is running, and wait for the next key ... next_char = main_greenlet.switch('blocking in read_next_char') ... return next_char Having defined both functions, we can start the ``process_commands`` greenlet, which will make it to ``read_next_char()`` and immediately switch back to the main greenlet: .. doctest:: >>> g_processor.switch() 'blocking in read_next_char' Now we can hand control over to the main event loop of the GUI. Of course, in documentation we don't have a GUI, so we'll fake one that feeds keys to ``event_keydown``; for demonstration purposes we'll also fake a ``process_command`` function that just prints the line it got. .. doctest:: >>> def process_command(line): ... print('(Processing command: ' + line.strip() + ')') >>> def gui_mainloop(): ... # The user types "hello" ... for c in 'hello\n': ... event_keydown(c) ... # The user types "quit" ... for c in 'quit\n': ... event_keydown(c) ... # The user responds to the prompt with 'y' ... event_keydown('y') >>> gui_mainloop() <<< hello (Processing command: hello) <<< quit Are you sure? <<< y (Exiting loop.) >>> g_processor.dead True .. sidebar:: Switching Isn't Contagious Notice how a single call to ``gui_mainloop`` successfully switched back and forth between two greenlets without the caller or author of ``gui_mainloop`` needing to be aware of that. Contrast this with :mod:`asyncio`, where the keywords ``async def`` and ``await`` often spread throughout the codebase once introduced. In fact, greenlets can be used to put a halt to that spread and execute ``async def`` code in a synchronous fashion. .. seealso:: For the interactions between :mod:`contextvars` and greenlets. :doc:`contextvars` In this example, the execution flow is: when ``read_next_char()`` is called, it is part of the ``g_processor`` greenlet, so when it switches to its parent greenlet, it resumes execution in the top-level main loop (the GUI). When the GUI calls ``event_keydown()``, it switches to ``g_processor``, which means that the execution jumps back wherever it was suspended in that greenlet |---| in this case, to the ``switch()`` instruction in ``read_next_char()`` |---| and the ``key`` argument in ``event_keydown()`` is passed as the return value of the switch() in ``read_next_char()``. Note that ``read_next_char()`` will be suspended and resumed with its call stack preserved, so that it will itself return to different positions in ``process_commands()`` depending on where it was originally called from. This allows the logic of the program to be kept in a nice control-flow way; we don't have to completely rewrite ``process_commands()`` to turn it into a state machine. Further Reading =============== Continue reading with :doc:`greenlet`. Curious how execution resumed in the main greenlet after ``process_commands`` exited its loop (and never explicitly switched back to the main greenlet)? Read about :ref:`greenlet_parents`. .. rubric:: Footnotes .. [#f1] You might try to run the GUI event loop in one thread, and the ``process_commands`` function in another thread. You could then use a thread-safe :class:`queue.Queue` to exchange keypresses between the two: write to the queue in ``event_keydown``, read from it in ``read_next_char``. One problem with this, though, is that many GUI toolkits are single-threaded and only run in the main thread, so we'd also need a way to communicate any results of ``process_command`` back to the main thread in order to update the GUI. We're now significantly diverging from our simple console-based application. python-greenlet-1.1.2/docs/history.rst000066400000000000000000000001271414024117000200140ustar00rootroot00000000000000=================== History And About =================== .. include:: ../README.rst python-greenlet-1.1.2/docs/index.rst000066400000000000000000000124671414024117000174340ustar00rootroot00000000000000============================================== greenlet: Lightweight concurrent programming ============================================== .. TODO: Divide into a few different kinds of documentation (https://documentation.divio.com/explanation/): - Tutorial, - API reference - how-to. - Explanation. Each document should identify what role it fulfills. .. |--| unicode:: U+2013 .. en dash .. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace :trim: .. sidebar:: Contents If this page has piqued your interest in greenlets, continue reading by seeing :ref:`an example transforming an asynchronous GUI into a simple synchronous loop `. To get started building your own code with greenlets, read :doc:`greenlet`, and then :doc:`creating_executing_greenlets`. .. toctree:: :caption: Getting Started :maxdepth: 2 gui_example greenlet creating_executing_greenlets switching .. toctree:: :maxdepth: 1 :caption: Reference Material api c_api changes development history .. toctree:: :maxdepth: 1 :caption: Advanced Usage python_threads contextvars greenlet_gc tracing caveats .. rubric:: What are greenlets? greenlets are lightweight coroutines for in-process sequential concurrent programming. greenlets can be used on their own, but they are frequently used with frameworks such as `gevent`_ to provide higher-level abstractions and asynchronous I/O. greenlets are frequently defined by analogy to :mod:`threads ` or Python's built-in coroutines (generators and ``async def`` functions). The rest of this section will explore those analogies. For a more precise introduction, see :ref:`what_is_greenlet`. See :doc:`history` for how the greenlet library was created, and its relation to other similar concepts. .. rubric:: Are greenlets similar to threads? For many purposes, you can usually think of greenlets as cooperatively scheduled :mod:`threads `. The major differences are that since they're cooperatively scheduled, you are in control of when they execute, and since they are coroutines, many greenlets can exist in a single native thread. .. rubric:: How are greenlets different from threads? Threads (in theory) are preemptive and parallel [#f1]_, meaning that multiple threads can be processing work at the same time, and it's impossible to say in what order different threads will proceed or see the effects of other threads. This necessitates careful programming using :class:`locks `, :class:`queues `, or other approaches to avoid `race conditions`_, `deadlocks`_, or other bugs. In contrast, greenlets are cooperative and sequential. This means that when one greenlet is running, no other greenlet can be running; the programmer is fully in control of when execution switches between greenlets. This can eliminate race conditions and greatly simplify the programming task. Also, threads require resources from the operating system (the thread stack, and bookkeeping in the kernel). Because greenlets are implemented entirely without involving the operating system, they can require fewer resources; it is often practical to have many more greenlets than it is threads. .. _race conditions: https://en.wikipedia.org/wiki/Race_condition .. _deadlocks: https://docs.microsoft.com/en-us/troubleshoot/dotnet/visual-basic/race-conditions-deadlocks#when-deadlocks-occur .. rubric:: How else can greenlets be used? greenlets have many uses: - They can be treated like cooperative threads. You can implement any scheduling policy you desire. - Because greenlets work well with C libraries (greenlets can switch even with C functions in the call stack), they are well suited for integrating with GUIs or other event loops. `gevent`_ is an example of using greenlets to integrate with IO event loops (`libev`_ and `libuv`_) to provide a complete asynchronous environment using familiar programming patterns. - Similar to the above, greenlets can be used to transform apparently asynchronous tasks into a simple synchronous style. See :ref:`gui_example` for an example of writing an asynchronous event-driven GUI app in a simple synchronous style. - In general, greenlets can be used for advanced control flow. For example, you can :doc:`create generators ` |---| without the use of the ``yield`` keyword! .. _gevent: https://www.gevent.org .. _libev: http://software.schmorp.de/pkg/libev.html .. _libuv: http://libuv.org/ .. rubric:: Are greenlets similar to generators? What about asyncio? All three of greenlets, generators, and asyncio use a concept of coroutines. However, greenlets, unlike the other two, require no special keywords or support from the Python language. In addition, greenlets are capable of switching between stacks that feature C libraries, whereas the other two are not. .. rubric:: Footnotes .. [#f1] In CPython, the `global interpreter lock (GIL) `_ generally prevents two threads from executing Python code at the same time. Parallelism is thus limited to code sections that release the GIL, i.e., C code. Indices and tables ================== * :ref:`search` * :ref:`genindex` * :ref:`modindex` python-greenlet-1.1.2/docs/make.bat000066400000000000000000000117541414024117000171760ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\greenlet.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\greenlet.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end python-greenlet-1.1.2/docs/python_threads.rst000066400000000000000000000022651414024117000213530ustar00rootroot00000000000000============================== Greenlets and Python Threads ============================== Greenlets can be combined with Python threads; in this case, each thread contains an independent "main" greenlet with a tree of sub-greenlets. It is not possible to mix or switch between greenlets belonging to different threads. .. doctest:: >>> from greenlet import getcurrent >>> from threading import Thread >>> from threading import Event >>> started = Event() >>> switched = Event() >>> class T(Thread): ... def run(self): ... self.glet = getcurrent() ... started.set() ... switched.wait() >>> t = T() >>> t.start() >>> _ = started.wait() >>> t.glet.switch() Traceback (most recent call last): ... greenlet.error: cannot switch to a different thread >>> switched.set() >>> t.join() Note that when a thread dies, the thread's main greenlet is not considered to be dead. .. doctest:: >>> t.glet.dead False .. caution:: For these reasons, it's best to not pass references to a greenlet running in one thread to another thread. If you do, take caution to carefully manage the lifetime of the references. python-greenlet-1.1.2/docs/switching.rst000066400000000000000000000137201414024117000203150ustar00rootroot00000000000000.. _switching: ========================================================== Switching Between Greenlets: Passing Objects and Control ========================================================== .. This is an "explanation" document. .. currentmodule:: greenlet Switches between greenlets occur when: - The method `greenlet.switch` of a greenlet is called, in which case execution jumps to the greenlet whose ``switch()`` is called; or - When the method `greenlet.throw` is used to raise an exception in the target greenlet, in which case execution jumps to the greenlet whose ``throw`` was called; or - When a greenlet dies, in which case execution jumps to the parent greenlet. During a switch, an object or an exception is "sent" to the target greenlet; this can be used as a convenient way to pass information between greenlets. For example: .. doctest:: >>> from greenlet import greenlet >>> def test1(x, y): ... z = gr2.switch(x + y) ... print(z) >>> def test2(u): ... print(u) ... gr1.switch(42) >>> gr1 = greenlet(test1) >>> gr2 = greenlet(test2) >>> gr1.switch("hello", " world") hello world 42 This prints "hello world" and 42. Note that the arguments of ``test1()`` and ``test2()`` are not provided when the greenlet is created, but only the first time someone switches to it. Here are the precise rules for sending objects around: ``g.switch(*args, **kwargs)`` Switches execution to the greenlet ``g``, sending it the given arguments. As a special case, if ``g`` did not start yet, then it will start to run now; ``args`` and ``kwargs`` are passed to the greenlet's ``run()`` function as its arguments. Dying greenlet If a greenlet's ``run()`` finishes, its return value is the object sent to its parent. If ``run()`` terminates with an exception, the exception is propagated to its parent (unless it is a ``greenlet.GreenletExit`` exception, in which case the exception object is caught and *returned* to the parent). Apart from the cases described above, the target greenlet normally receives the object as the return value of the call to ``switch()`` in which it was previously suspended. Indeed, although a call to ``switch()`` does not return immediately, it will still return at some point in the future, when some other greenlet switches back. When this occurs, then execution resumes just after the ``switch()`` where it was suspended, and the ``switch()`` itself appears to return the object that was just sent. This means that ``x = g.switch(y)`` will send the object ``y`` to ``g``, and will later put the (unrelated) object that some (unrelated) greenlet passes back to us into ``x``. Multiple And Keyword Arguments ============================== You can pass multiple or keyword arguments to ``switch()``. If the greenlet hasn't begun running, those are passed as function arguments to ``run`` as usual in Python. If the greenlet *was* running, multiple arguments will be a :class:`tuple`, and keyword arguments will be a :class:`dict`; any number of positional arguments with keyword arguments will have the entire set in a tuple, with positional arguments in their own nested tuple, and keyword arguments as a `dict` in the the last element of the tuple: .. doctest:: >>> def test1(x, y, **kwargs): ... while 1: ... z = gr2.switch(x + y + ' ' + str(kwargs)) ... if not z: break ... print(z) >>> def test2(u): ... print(u) ... # A single argument -> itself ... gr1.switch(42) ... # Multiple positional args -> a tuple ... gr1.switch("how", "are", "you") ... # Only keyword arguments -> a dict ... gr1.switch(language='en') ... # one positional and keywords -> ((tuple,), dict) ... gr1.switch("howdy", language='en_US') ... # multiple positional and keywords -> ((tuple,), dict) ... gr1.switch("all", "y'all", language='en_US_OK') ... gr1.switch(None) # terminate >>> gr1 = greenlet(test1) >>> gr2 = greenlet(test2) >>> gr1.switch("hello", " world", language='en') hello world {'language': 'en'} 42 ('how', 'are', 'you') {'language': 'en'} (('howdy',), {'language': 'en_US'}) (('all', "y'all"), {'language': 'en_US_OK'}) .. _switch_to_dead: Switching To Dead Greenlets =========================== Note that any attempt to switch to a dead greenlet actually goes to the dead greenlet's parent, or its parent's parent, and so on. (The final parent is the "main" greenlet, which is never dead.) .. doctest:: >>> def inner(): ... print("Entering inner.") ... print("Returning from inner.") ... return 42 >>> def outer(): ... print("Entering outer and spawning inner.") ... inner_glet = greenlet(inner) ... print("Switching to inner.") ... result = inner_glet.switch() ... print("Got from inner value: %s" % (result,)) ... print("Switching to inner again.") ... result = inner_glet.switch() ... print("Got from inner value: %s" % (result,)) ... return inner_glet >>> outer_glet = greenlet(outer) Here, our main greenlet has created another greenlet (``outer``), which in turn creates a greenlet (``inner``). The outer greenlet switches to the inner greenlet, which immediately finishes and dies; the outer greenlet attempts to switch back to the inner greenlet, but since the inner greenlet is dead, it just switches...to itself (since it was the parent). Note how the second switch (to the dead greenlet) returns an empty tuple. .. doctest:: >>> inner_glet = outer_glet.switch() Entering outer and spawning inner. Switching to inner. Entering inner. Returning from inner. Got from inner value: 42 Switching to inner again. Got from inner value: () We can similarly ask the main greenlet to switch to the (dead) inner greenlet and its (dead) parent and wind up still in the main greenlet. >>> inner_glet.switch() () python-greenlet-1.1.2/docs/tracing.rst000066400000000000000000000056201414024117000177450ustar00rootroot00000000000000======================= Tracing And Profiling ======================= .. currentmodule:: greenlet Standard Python tracing and profiling doesn't work as expected when used with greenlet since stack and frame switching happens on the same Python thread. It is difficult to detect greenlet switching reliably with conventional methods, so to improve support for debugging, tracing and profiling greenlet based code there are new functions in the greenlet module, `gettrace` and `settrace`. Trace Callback Functions ======================== Trace callback functions are installed using `settrace` and must have the signature ``callback(event: str, args: Any)``. .. important:: For compatibility it is very important to unpack ``args`` tuple only when ``event`` is one of those defined here, and not when ``event`` is potentially something else. This way API can be extended to new events similar to :func:`sys.settrace()`. The parameter *event* is a string naming what happened. The following events are defined: ``switch`` In this case, ``args`` is a two-tuple ``(origin, target)``. Called to handle a switch from ``origin`` to ``target``. Note that callback is running in the context of the ``target`` greenlet and any exceptions will be passed as if ``target.throw()`` was used instead of a switch. ``throw`` In this case, ``args`` is a two-tuple ``(origin, target)``. Called to handle a throw from ``origin`` to ``target``. Note that callback is running in the context of ``target`` greenlet and any exceptions will replace the original, as if ``target.throw()`` was used with the replacing exception. For example: .. doctest:: >>> import greenlet >>> def callback(event, args): ... if event in ('switch', 'throw'): ... origin, target = args ... print("Transfer from %s to %s with %s" ... % (origin, target, event)) >>> class Origin(greenlet.greenlet): ... def run(self): ... print("In origin") ... target.switch() ... print("Returned to origin") ... target.throw() ... def __str__(self): ... return "" >>> class Target(greenlet.greenlet): ... def run(self): ... origin.switch() ... def __str__(self): ... return "" >>> old_trace = greenlet.settrace(callback) >>> origin = Origin() >>> target = Target() >>> _ = origin.switch() Transfer from to with switch In origin Transfer from to with switch Transfer from to with switch Returned to origin Transfer from to with throw Transfer from to with switch Of course, when we're done, it's important to restore the previous state: .. doctest:: >>> _ = greenlet.settrace(old_trace) python-greenlet-1.1.2/make-manylinux000077500000000000000000000030361414024117000175200ustar00rootroot00000000000000#!/bin/bash # Initially based on a snippet from the greenlet project. # This needs to be run from the root of the project. # To update: docker pull quay.io/pypa/manylinux2010_x86_64 set -e export PYTHONUNBUFFERED=1 export PYTHONDONTWRITEBYTECODE=1 # Use a fixed hash seed for reproducability export PYTHONHASHSEED=8675309 export CI=1 export TRAVIS=true # Don't get warnings about Python 2 support being deprecated. We # know. The env var works for pip 20. export PIP_NO_PYTHON_VERSION_WARNING=1 export PIP_NO_WARN_SCRIPT_LOCATION=1 if [ -d /greenlet -a -d /opt/python ]; then # Running inside docker # Build in an isolated directory mkdir /tmp/build cd /tmp/build git clone /greenlet greenlet cd greenlet mkdir -p /greenlet/wheelhouse OPATH="$PATH" which auditwheel for variant in `ls -d /opt/python/cp{27,35,36,37,38,39,310}*`; do export PATH="$variant/bin:$OPATH" echo "Building $variant $(python --version)" python -mpip install -U pip python setup.py bdist_wheel python -mpip install -U . python -m unittest discover -v greenlet.tests PATH="$OPATH" auditwheel repair dist/greenlet*.whl cp wheelhouse/greenlet*.whl /greenlet/wheelhouse rm -rf build rm -f dist/greenlet*.whl done exit 0 fi # Mount the current directory as /greenlet # Can't use -i on Travis with arm64, "input device not a tty" docker run --rm -v "$(pwd):/greenlet" ${DOCKER_IMAGE:-quay.io/pypa/manylinux2010_x86_64} /greenlet/$(basename $0) ls -l wheelhouse python-greenlet-1.1.2/setup.cfg000066400000000000000000000001261414024117000164510ustar00rootroot00000000000000[zest.releaser] python-file-with-version = src/greenlet/__init__.py create-wheel = no python-greenlet-1.1.2/setup.py000077500000000000000000000132341414024117000163510ustar00rootroot00000000000000#! /usr/bin/env python import sys import os import glob import platform # distutils is deprecated and vendored into setuptools now. from setuptools import setup from setuptools import Extension from setuptools import find_packages # workaround segfaults on openbsd and RHEL 3 / CentOS 3 . see # https://bitbucket.org/ambroff/greenlet/issue/11/segfault-on-openbsd-i386 # https://github.com/python-greenlet/greenlet/issues/4 # https://github.com/python-greenlet/greenlet/issues/94 # pylint:disable=too-many-boolean-expressions if ((sys.platform == "openbsd4" and os.uname()[-1] == "i386") or ("-with-redhat-3." in platform.platform() and platform.machine() == 'i686') or (sys.platform == "sunos5" and os.uname()[-1] == "sun4v") or ("SunOS" in platform.platform() and platform.machine() == "sun4v") or (sys.platform == "linux" and platform.machine() == "ppc")): os.environ["CFLAGS"] = ("%s %s" % (os.environ.get("CFLAGS", ""), "-Os")).lstrip() def readfile(filename): with open(filename, 'r') as f: return f.read() GREENLET_SRC_DIR = 'src/greenlet/' GREENLET_HEADER_DIR = GREENLET_SRC_DIR GREENLET_HEADER = GREENLET_HEADER_DIR + 'greenlet.h' GREENLET_TEST_DIR = 'src/greenlet/tests/' # The location of the platform specific assembly files # for switching. GREENLET_PLATFORM_DIR = GREENLET_SRC_DIR + 'platform/' def _find_platform_headers(): return glob.glob(GREENLET_PLATFORM_DIR + "switch_*.h") if hasattr(sys, "pypy_version_info"): ext_modules = [] headers = [] else: headers = [GREENLET_HEADER] if sys.platform == 'win32' and '64 bit' in sys.version: # this works when building with msvc, not with 64 bit gcc # switch_x64_masm.obj can be created with setup_switch_x64_masm.cmd extra_objects = [GREENLET_PLATFORM_DIR + 'switch_x64_masm.obj'] else: extra_objects = [] if sys.platform == 'win32' and os.environ.get('GREENLET_STATIC_RUNTIME') in ('1', 'yes'): extra_compile_args = ['/MT'] elif hasattr(os, 'uname') and os.uname()[4] in ['ppc64el', 'ppc64le']: extra_compile_args = ['-fno-tree-dominator-opts'] else: extra_compile_args = [] ext_modules = [ Extension( name='greenlet._greenlet', sources=[GREENLET_SRC_DIR + 'greenlet.c'], extra_objects=extra_objects, extra_compile_args=extra_compile_args, depends=[ GREENLET_HEADER, GREENLET_SRC_DIR + 'slp_platformselect.h', ] + _find_platform_headers() ), # Test extensions. # # We used to try hard to not include these in built # distributions, because we only distributed ``greenlet.so``. # That's really not important, now we have a clean layout with # the test directory nested inside a greenlet directory. See # https://github.com/python-greenlet/greenlet/issues/184 and # 189 Extension( name='greenlet.tests._test_extension', sources=[GREENLET_TEST_DIR + '_test_extension.c'], include_dirs=[GREENLET_HEADER_DIR] ), ] if os.environ.get('GREENLET_TEST_CPP', 'yes').lower() not in ('0', 'no', 'false'): ext_modules.append( Extension( name='greenlet.tests._test_extension_cpp', sources=[GREENLET_TEST_DIR + '_test_extension_cpp.cpp'], language="c++", include_dirs=[GREENLET_HEADER_DIR]), ) def get_greenlet_version(): with open('src/greenlet/__init__.py') as f: looking_for = '__version__ = \'' for line in f: if line.startswith(looking_for): version = line[len(looking_for):-2] return version raise ValueError("Unable to find version") setup( name="greenlet", version=get_greenlet_version(), description='Lightweight in-process concurrent programming', long_description=readfile("README.rst"), long_description_content_type="text/x-rst", url="https://greenlet.readthedocs.io/", keywords="greenlet coroutine concurrency threads cooperative", author="Alexey Borzenkov", author_email="snaury@gmail.com", maintainer='Jason Madden', maintainer_email='jason@nextthought.com', project_urls={ 'Bug Tracker': 'https://github.com/python-greenlet/greenlet/issues', 'Source Code': 'https://github.com/python-greenlet/greenlet/', 'Documentation': 'https://greenlet.readthedocs.io/', }, license="MIT License", platforms=['any'], package_dir={'': 'src'}, packages=find_packages('src'), include_package_data=True, headers=headers, ext_modules=ext_modules, classifiers=[ "Development Status :: 5 - Production/Stable", 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Programming Language :: C', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules' ], extras_require={ 'docs': [ 'Sphinx', ], 'test': [ ], }, python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*", zip_safe=False, ) python-greenlet-1.1.2/src/000077500000000000000000000000001414024117000154205ustar00rootroot00000000000000python-greenlet-1.1.2/src/greenlet/000077500000000000000000000000001414024117000172255ustar00rootroot00000000000000python-greenlet-1.1.2/src/greenlet/__init__.py000066400000000000000000000024531414024117000213420ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ The root of the greenlet package. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function __all__ = [ '__version__', '_C_API', 'GreenletExit', 'error', 'getcurrent', 'greenlet', 'gettrace', 'settrace', ] # pylint:disable=no-name-in-module ### # Metadata ### __version__ = '1.1.2' from ._greenlet import _C_API # pylint:disable=no-name-in-module ### # Exceptions ### from ._greenlet import GreenletExit from ._greenlet import error ### # greenlets ### from ._greenlet import getcurrent from ._greenlet import greenlet ### # tracing ### try: from ._greenlet import gettrace from ._greenlet import settrace except ImportError: # Tracing wasn't supported. # XXX: The option to disable it was removed in 1.0, # so this branch should be dead code. pass ### # Constants # These constants aren't documented and aren't recommended. # In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS # is the same as ``sys.version_info[:2] >= 3.7`` ### from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import python-greenlet-1.1.2/src/greenlet/greenlet.c000066400000000000000000002007141414024117000212020ustar00rootroot00000000000000/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ /* Format with: * clang-format -i --style=file src/greenlet/greenlet.c * * * Fix missing braces with: * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" */ #define GREENLET_MODULE #include "greenlet.h" #include "structmember.h" #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-parameter" # pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif /*********************************************************** A PyGreenlet is a range of C stack addresses that must be saved and restored in such a way that the full range of the stack contains valid data when we switch to it. Stack layout for a greenlet: | ^^^ | | older data | | | stack_stop . |_______________| . | | . | greenlet data | . | in stack | . * |_______________| . . _____________ stack_copy + stack_saved . | | | | . | data | |greenlet data| . | unrelated | | saved | . | to | | in heap | stack_start . | this | . . |_____________| stack_copy | greenlet | | | | newer data | | vvv | Note that a greenlet's stack data is typically partly at its correct place in the stack, and partly saved away in the heap, but always in the above configuration: two blocks, the more recent one in the heap and the older one still in the stack (either block may be empty). Greenlets are chained: each points to the previous greenlet, which is the one that owns the data currently in the C stack above my stack_stop. The currently running greenlet is the first element of this chain. The main (initial) greenlet is the last one. Greenlets whose stack is entirely in the heap can be skipped from the chain. The chain is not related to execution order, but only to the order in which bits of C stack happen to belong to greenlets at a particular point in time. The main greenlet doesn't have a stack_stop: it is responsible for the complete rest of the C stack, and we don't know where it begins. We use (char*) -1, the largest possible address. States: stack_stop == NULL && stack_start == NULL: did not start yet stack_stop != NULL && stack_start == NULL: already finished stack_stop != NULL && stack_start != NULL: active The running greenlet's stack_start is undefined but not NULL. ***********************************************************/ /*** global state ***/ /* In the presence of multithreading, this is a bit tricky: - ts_current always store a reference to a greenlet, but it is not really the current greenlet after a thread switch occurred. - each *running* greenlet uses its run_info field to know which thread it is attached to. A greenlet can only run in the thread where it was created. This run_info is a ref to tstate->dict. - the thread state dict is used to save and restore ts_current, using the dictionary key 'ts_curkey'. */ extern PyTypeObject PyGreenlet_Type; #if PY_VERSION_HEX >= 0x030700A3 # define GREENLET_PY37 1 #else # define GREENLET_PY37 0 #endif #if PY_VERSION_HEX >= 0x30A00B1 /* Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member. See https://github.com/python/cpython/pull/25276 We have to save and restore this as well. */ #define TSTATE_USE_TRACING(tstate) (tstate->cframe->use_tracing) #define GREENLET_USE_CFRAME 1 #else #define TSTATE_USE_TRACING(tstate) (tstate->use_tracing) #define GREENLET_USE_CFRAME 0 #endif #ifndef Py_SET_REFCNT /* Py_REFCNT and Py_SIZE macros are converted to functions https://bugs.python.org/issue39573 */ # define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) #endif #ifndef _Py_DEC_REFTOTAL /* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 */ # ifdef Py_REF_DEBUG # define _Py_DEC_REFTOTAL _Py_RefTotal-- # else # define _Py_DEC_REFTOTAL # endif #endif /* Weak reference to the switching-to greenlet during the slp switch */ static PyGreenlet* volatile ts_target = NULL; /* Strong reference to the switching from greenlet after the switch */ static PyGreenlet* volatile ts_origin = NULL; /* Strong reference to the current greenlet in this thread state */ static PyGreenlet* volatile ts_current = NULL; /* NULL if error, otherwise args tuple to pass around during slp switch */ static PyObject* volatile ts_passaround_args = NULL; static PyObject* volatile ts_passaround_kwargs = NULL; /* Used internally in ``g_switchstack()`` */ #if GREENLET_USE_CFRAME static int volatile ts__g_switchstack_use_tracing = 0; #endif /***********************************************************/ /* Thread-aware routines, switching global variables when needed */ #define STATE_OK \ (ts_current->run_info == PyThreadState_GET()->dict || \ !green_updatecurrent()) static PyObject* ts_curkey; static PyObject* ts_delkey; static PyObject* ts_tracekey; static PyObject* ts_event_switch; static PyObject* ts_event_throw; static PyObject* PyExc_GreenletError; static PyObject* PyExc_GreenletExit; static PyObject* ts_empty_tuple; static PyObject* ts_empty_dict; #define GREENLET_GC_FLAGS Py_TPFLAGS_HAVE_GC #define GREENLET_tp_alloc PyType_GenericAlloc #define GREENLET_tp_free PyObject_GC_Del #define GREENLET_tp_traverse green_traverse #define GREENLET_tp_clear green_clear #define GREENLET_tp_is_gc green_is_gc static void green_clear_exc(PyGreenlet* g) { #if GREENLET_PY37 g->exc_info = NULL; g->exc_state.exc_type = NULL; g->exc_state.exc_value = NULL; g->exc_state.exc_traceback = NULL; g->exc_state.previous_item = NULL; #else g->exc_type = NULL; g->exc_value = NULL; g->exc_traceback = NULL; #endif } static PyGreenlet* green_create_main(void) { PyGreenlet* gmain; PyObject* dict = PyThreadState_GetDict(); if (dict == NULL) { if (!PyErr_Occurred()) { PyErr_NoMemory(); } return NULL; } /* create the main greenlet for this thread */ gmain = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0); if (gmain == NULL) { return NULL; } gmain->stack_start = (char*)1; gmain->stack_stop = (char*)-1; /* GetDict() returns a borrowed reference. Make it strong. */ gmain->run_info = dict; Py_INCREF(dict); return gmain; } static int green_updatecurrent(void) { PyObject *exc, *val, *tb; PyThreadState* tstate; PyGreenlet* current; PyGreenlet* previous; PyObject* deleteme; green_updatecurrent_restart: /* save current exception */ PyErr_Fetch(&exc, &val, &tb); /* get ts_current from the active tstate */ tstate = PyThreadState_GET(); if (tstate->dict && (current = (PyGreenlet*)PyDict_GetItem(tstate->dict, ts_curkey))) { /* found -- remove it, to avoid keeping a ref */ Py_INCREF(current); PyDict_DelItem(tstate->dict, ts_curkey); } else { /* first time we see this tstate */ current = green_create_main(); if (current == NULL) { Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } } assert(current->run_info == tstate->dict); green_updatecurrent_retry: /* update ts_current as soon as possible, in case of nested switches */ Py_INCREF(current); previous = ts_current; ts_current = current; /* save ts_current as the current greenlet of its own thread */ if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*)previous)) { Py_DECREF(previous); Py_DECREF(current); Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } Py_DECREF(previous); /* green_dealloc() cannot delete greenlets from other threads, so it stores them in the thread dict; delete them now. */ deleteme = PyDict_GetItem(tstate->dict, ts_delkey); if (deleteme != NULL) { /* The only reference to these greenlets should be in this list, so clearing the list should let them be deleted again, triggering calls to green_dealloc() in the correct thread. This may run arbitrary Python code? */ PyList_SetSlice(deleteme, 0, INT_MAX, NULL); } if (ts_current != current) { /* some Python code executed above and there was a thread switch, * so ts_current points to some other thread again. We need to * delete ts_curkey (it's likely there) and retry. */ PyDict_DelItem(tstate->dict, ts_curkey); goto green_updatecurrent_retry; } /* release an extra reference */ Py_DECREF(current); /* restore current exception */ PyErr_Restore(exc, val, tb); /* thread switch could happen during PyErr_Restore, in that case there's nothing to do except restart from scratch. */ if (ts_current->run_info != tstate->dict) { goto green_updatecurrent_restart; } return 0; } static PyObject* green_statedict(PyGreenlet* g) { while (!PyGreenlet_STARTED(g)) { g = g->parent; if (g == NULL) { /* garbage collected greenlet in chain */ return NULL; } } return g->run_info; } /***********************************************************/ /* Some functions must not be inlined: * slp_restore_state, when inlined into slp_switch might cause it to restore stack over its own local variables * slp_save_state, when inlined would add its own local variables to the saved stack, wasting space * slp_switch, cannot be inlined for obvious reasons * g_initialstub, when inlined would receive a pointer into its own stack frame, leading to incomplete stack save/restore */ #if defined(__GNUC__) && \ (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define GREENLET_NOINLINE_SUPPORTED # define GREENLET_NOINLINE(name) __attribute__((noinline)) name #elif defined(_MSC_VER) && (_MSC_VER >= 1300) # define GREENLET_NOINLINE_SUPPORTED # define GREENLET_NOINLINE(name) __declspec(noinline) name #endif #ifdef GREENLET_NOINLINE_SUPPORTED /* add forward declarations */ static void GREENLET_NOINLINE(slp_restore_state)(void); static int GREENLET_NOINLINE(slp_save_state)(char*); # if !(defined(MS_WIN64) && defined(_M_X64)) static int GREENLET_NOINLINE(slp_switch)(void); # endif static int GREENLET_NOINLINE(g_initialstub)(void*); # define GREENLET_NOINLINE_INIT() \ do { \ } while (0) #else /* force compiler to call functions via pointers */ static void (*slp_restore_state)(void); static int (*slp_save_state)(char*); static int (*slp_switch)(void); static int (*g_initialstub)(void*); # define GREENLET_NOINLINE(name) cannot_inline_##name # define GREENLET_NOINLINE_INIT() \ do { \ slp_restore_state = GREENLET_NOINLINE(slp_restore_state); \ slp_save_state = GREENLET_NOINLINE(slp_save_state); \ slp_switch = GREENLET_NOINLINE(slp_switch); \ g_initialstub = GREENLET_NOINLINE(g_initialstub); \ } while (0) #endif /* * the following macros are spliced into the OS/compiler * specific code, in order to simplify maintenance. */ #define SLP_SAVE_STATE(stackref, stsizediff) \ stackref += STACK_MAGIC; \ if (slp_save_state((char*)stackref)) \ return -1; \ if (!PyGreenlet_ACTIVE(ts_target)) \ return 1; \ stsizediff = ts_target->stack_start - (char*)stackref #define SLP_RESTORE_STATE() slp_restore_state() #define SLP_EVAL #define slp_switch GREENLET_NOINLINE(slp_switch) #include "slp_platformselect.h" #undef slp_switch #ifndef STACK_MAGIC # error \ "greenlet needs to be ported to this platform, or taught how to detect your compiler properly." #endif /* !STACK_MAGIC */ #ifdef EXTERNAL_ASM /* CCP addition: Make these functions, to be called from assembler. * The token include file for the given platform should enable the * EXTERNAL_ASM define so that this is included. */ intptr_t slp_save_state_asm(intptr_t* ref) { intptr_t diff; SLP_SAVE_STATE(ref, diff); return diff; } void slp_restore_state_asm(void) { SLP_RESTORE_STATE(); } extern int slp_switch(void); #endif /***********************************************************/ static int g_save(PyGreenlet* g, char* stop) { /* Save more of g's stack into the heap -- at least up to 'stop' g->stack_stop |________| | | | __ stop . . . . . | | ==> . . |________| _______ | | | | | | | | g->stack_start | | |_______| g->stack_copy */ intptr_t sz1 = g->stack_saved; intptr_t sz2 = stop - g->stack_start; assert(g->stack_start != NULL); if (sz2 > sz1) { char* c = (char*)PyMem_Realloc(g->stack_copy, sz2); if (!c) { PyErr_NoMemory(); return -1; } memcpy(c + sz1, g->stack_start + sz1, sz2 - sz1); g->stack_copy = c; g->stack_saved = sz2; } return 0; } static void GREENLET_NOINLINE(slp_restore_state)(void) { PyGreenlet* g = ts_target; PyGreenlet* owner = ts_current; #ifdef SLP_BEFORE_RESTORE_STATE SLP_BEFORE_RESTORE_STATE(); #endif /* Restore the heap copy back into the C stack */ if (g->stack_saved != 0) { memcpy(g->stack_start, g->stack_copy, g->stack_saved); PyMem_Free(g->stack_copy); g->stack_copy = NULL; g->stack_saved = 0; } if (owner->stack_start == NULL) { owner = owner->stack_prev; /* greenlet is dying, skip it */ } while (owner && owner->stack_stop <= g->stack_stop) { owner = owner->stack_prev; /* find greenlet with more stack */ } g->stack_prev = owner; } static int GREENLET_NOINLINE(slp_save_state)(char* stackref) { /* must free all the C stack up to target_stop */ char* target_stop = ts_target->stack_stop; PyGreenlet* owner = ts_current; assert(owner->stack_saved == 0); if (owner->stack_start == NULL) { owner = owner->stack_prev; /* not saved if dying */ } else { owner->stack_start = stackref; } #ifdef SLP_BEFORE_SAVE_STATE SLP_BEFORE_SAVE_STATE(); #endif while (owner->stack_stop < target_stop) { /* ts_current is entierely within the area to free */ if (g_save(owner, owner->stack_stop)) { return -1; /* XXX */ } owner = owner->stack_prev; } if (owner != ts_target) { if (g_save(owner, target_stop)) { return -1; /* XXX */ } } return 0; } /** Perform a stack switch according to some global variables that must be set before calling this function. Those variables are: - ts_current: current greenlet (holds a reference) - ts_target: greenlet to switch to (weak reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_target (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) Because the stack switch happens in this function, this function can't use its own stack (local) variables, set before the switch, and then accessed after the switch. Global variables beginning with ``ts__g_switchstack`` are used internally instead. On return results are passed via global variables as well: - ts_origin: originating greenlet (holds a reference) - ts_current: current greenlet (holds a reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_current (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) It is very important that stack switch is 'atomic', i.e. no calls into other Python code allowed (except very few that are safe), because global variables are very fragile. */ static int g_switchstack(void) { int err; { /* save state */ PyGreenlet* current = ts_current; PyThreadState* tstate = PyThreadState_GET(); current->recursion_depth = tstate->recursion_depth; current->top_frame = tstate->frame; #if GREENLET_PY37 current->context = tstate->context; #endif #if GREENLET_PY37 current->exc_info = tstate->exc_info; current->exc_state = tstate->exc_state; #else current->exc_type = tstate->exc_type; current->exc_value = tstate->exc_value; current->exc_traceback = tstate->exc_traceback; #endif #if GREENLET_USE_CFRAME /* IMPORTANT: ``cframe`` is a pointer into the STACK. Thus, because the call to ``slp_switch()`` changes the contents of the stack, you cannot read from ``ts_current->cframe`` after that call and necessarily get the same values you get from reading it here. Anything you need to restore from now to then must be saved in a global variable (because we can't use stack variables here either). */ current->cframe = tstate->cframe; ts__g_switchstack_use_tracing = tstate->cframe->use_tracing; #endif } err = slp_switch(); if (err < 0) { /* error */ PyGreenlet* current = ts_current; current->top_frame = NULL; #if GREENLET_PY37 green_clear_exc(current); #else current->exc_type = NULL; current->exc_value = NULL; current->exc_traceback = NULL; #endif assert(ts_origin == NULL); ts_target = NULL; } else { PyGreenlet* target = ts_target; PyGreenlet* origin = ts_current; PyThreadState* tstate = PyThreadState_GET(); tstate->recursion_depth = target->recursion_depth; tstate->frame = target->top_frame; target->top_frame = NULL; #if GREENLET_PY37 tstate->context = target->context; target->context = NULL; /* Incrementing this value invalidates the contextvars cache, which would otherwise remain valid across switches */ tstate->context_ver++; #endif #if GREENLET_PY37 tstate->exc_state = target->exc_state; tstate->exc_info = target->exc_info ? target->exc_info : &tstate->exc_state; #else tstate->exc_type = target->exc_type; tstate->exc_value = target->exc_value; tstate->exc_traceback = target->exc_traceback; #endif green_clear_exc(target); #if GREENLET_USE_CFRAME tstate->cframe = target->cframe; /* If we were tracing, we need to keep tracing. There should never be the possibility of hitting the root_cframe here. See note above about why we can't just copy this from ``origin->cframe->use_tracing``. */ tstate->cframe->use_tracing = ts__g_switchstack_use_tracing; #endif assert(ts_origin == NULL); Py_INCREF(target); ts_current = target; ts_origin = origin; ts_target = NULL; } return err; } static int g_calltrace(PyObject* tracefunc, PyObject* event, PyGreenlet* origin, PyGreenlet* target) { PyObject* retval; PyObject *exc_type, *exc_val, *exc_tb; PyThreadState* tstate; PyErr_Fetch(&exc_type, &exc_val, &exc_tb); tstate = PyThreadState_GET(); tstate->tracing++; TSTATE_USE_TRACING(tstate) = 0; retval = PyObject_CallFunction(tracefunc, "O(OO)", event, origin, target); tstate->tracing--; TSTATE_USE_TRACING(tstate) = (tstate->tracing <= 0 && ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL))); if (retval == NULL) { /* In case of exceptions trace function is removed */ if (PyDict_GetItem(tstate->dict, ts_tracekey)) { PyDict_DelItem(tstate->dict, ts_tracekey); } Py_XDECREF(exc_type); Py_XDECREF(exc_val); Py_XDECREF(exc_tb); return -1; } else { Py_DECREF(retval); } PyErr_Restore(exc_type, exc_val, exc_tb); return 0; } static PyObject* g_switch(PyGreenlet* target, PyObject* args, PyObject* kwargs) { /* _consumes_ a reference to the args tuple and kwargs dict, and return a new tuple reference */ int err = 0; PyObject* run_info; /* check ts_current */ if (!STATE_OK) { Py_XDECREF(args); Py_XDECREF(kwargs); return NULL; } run_info = green_statedict(target); if (run_info == NULL || run_info != ts_current->run_info) { Py_XDECREF(args); Py_XDECREF(kwargs); PyErr_SetString(PyExc_GreenletError, run_info ? "cannot switch to a different thread" : "cannot switch to a garbage collected greenlet"); return NULL; } ts_passaround_args = args; ts_passaround_kwargs = kwargs; /* find the real target by ignoring dead greenlets, and if necessary starting a greenlet. */ while (target) { if (PyGreenlet_ACTIVE(target)) { ts_target = target; err = g_switchstack(); break; } if (!PyGreenlet_STARTED(target)) { void* dummymarker; ts_target = target; err = g_initialstub(&dummymarker); if (err == 1) { continue; /* retry the switch */ } break; } target = target->parent; } /* For a very short time, immediately after the 'atomic' g_switchstack() call, global variables are in a known state. We need to save everything we need, before it is destroyed by calls into arbitrary Python code. */ args = ts_passaround_args; ts_passaround_args = NULL; kwargs = ts_passaround_kwargs; ts_passaround_kwargs = NULL; if (err < 0) { /* Turn switch errors into switch throws */ assert(ts_origin == NULL); Py_CLEAR(kwargs); Py_CLEAR(args); } else { PyGreenlet* origin; PyGreenlet* current; PyObject* tracefunc; origin = ts_origin; ts_origin = NULL; current = ts_current; if ((tracefunc = PyDict_GetItem(current->run_info, ts_tracekey)) != NULL) { Py_INCREF(tracefunc); if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, current) < 0) { /* Turn trace errors into switch throws */ Py_CLEAR(kwargs); Py_CLEAR(args); } Py_DECREF(tracefunc); } Py_DECREF(origin); } /* We need to figure out what values to pass to the target greenlet based on the arguments that have been passed to greenlet.switch(). If switch() was just passed an arg tuple, then we'll just return that. If only keyword arguments were passed, then we'll pass the keyword argument dict. Otherwise, we'll create a tuple of (args, kwargs) and return both. */ if (kwargs == NULL) { return args; } else if (PyDict_Size(kwargs) == 0) { Py_DECREF(kwargs); return args; } else if (PySequence_Length(args) == 0) { Py_DECREF(args); return kwargs; } else { PyObject* tuple = PyTuple_New(2); if (tuple == NULL) { Py_DECREF(args); Py_DECREF(kwargs); return NULL; } PyTuple_SET_ITEM(tuple, 0, args); PyTuple_SET_ITEM(tuple, 1, kwargs); return tuple; } } static PyObject* g_handle_exit(PyObject* result) { if (result == NULL && PyErr_ExceptionMatches(PyExc_GreenletExit)) { /* catch and ignore GreenletExit */ PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); if (val == NULL) { Py_INCREF(Py_None); val = Py_None; } result = val; Py_DECREF(exc); Py_XDECREF(tb); } if (result != NULL) { /* package the result into a 1-tuple */ PyObject* r = result; result = PyTuple_New(1); if (result) { PyTuple_SET_ITEM(result, 0, r); } else { Py_DECREF(r); } } return result; } static int GREENLET_NOINLINE(g_initialstub)(void* mark) { int err; PyObject *o, *run; PyObject *exc, *val, *tb; PyObject* run_info; PyGreenlet* self = ts_target; PyObject* args = ts_passaround_args; PyObject* kwargs = ts_passaround_kwargs; #if GREENLET_USE_CFRAME /* See green_new(). This is a stack-allocated variable used while *self* is in PyObject_Call(). We want to defer copying the state info until we're sure we need it and are in a stable place to do so. */ CFrame trace_info; #endif /* save exception in case getattr clears it */ PyErr_Fetch(&exc, &val, &tb); /* self.run is the object to call in the new greenlet */ run = PyObject_GetAttrString((PyObject*)self, "run"); if (run == NULL) { Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return -1; } /* restore saved exception */ PyErr_Restore(exc, val, tb); /* recheck the state in case getattr caused thread switches */ if (!STATE_OK) { Py_DECREF(run); return -1; } /* recheck run_info in case greenlet reparented anywhere above */ run_info = green_statedict(self); if (run_info == NULL || run_info != ts_current->run_info) { Py_DECREF(run); PyErr_SetString(PyExc_GreenletError, run_info ? "cannot switch to a different thread" : "cannot switch to a garbage collected greenlet"); return -1; } /* by the time we got here another start could happen elsewhere, * that means it should now be a regular switch */ if (PyGreenlet_STARTED(self)) { Py_DECREF(run); ts_passaround_args = args; ts_passaround_kwargs = kwargs; return 1; } #if GREENLET_USE_CFRAME /* OK, we need it, we're about to switch greenlets, save the state. */ trace_info = *PyThreadState_GET()->cframe; /* Make the target greenlet refer to the stack value. */ self->cframe = &trace_info; /* And restore the link to the previous frame so this one gets unliked appropriately. */ self->cframe->previous = &PyThreadState_GET()->root_cframe; #endif /* start the greenlet */ self->stack_start = NULL; self->stack_stop = (char*)mark; if (ts_current->stack_start == NULL) { /* ts_current is dying */ self->stack_prev = ts_current->stack_prev; } else { self->stack_prev = ts_current; } self->top_frame = NULL; green_clear_exc(self); self->recursion_depth = PyThreadState_GET()->recursion_depth; /* restore arguments in case they are clobbered */ ts_target = self; ts_passaround_args = args; ts_passaround_kwargs = kwargs; /* perform the initial switch */ err = g_switchstack(); /* returns twice! The 1st time with ``err == 1``: we are in the new greenlet The 2nd time with ``err <= 0``: back in the caller's greenlet */ if (err == 1) { /* in the new greenlet */ PyGreenlet* origin; PyObject* tracefunc; PyObject* result; PyGreenlet* parent; self->stack_start = (char*)1; /* running */ /* grab origin while we still can */ origin = ts_origin; ts_origin = NULL; /* now use run_info to store the statedict */ o = self->run_info; self->run_info = green_statedict(self->parent); Py_INCREF(self->run_info); Py_XDECREF(o); if ((tracefunc = PyDict_GetItem(self->run_info, ts_tracekey)) != NULL) { Py_INCREF(tracefunc); if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, self) < 0) { /* Turn trace errors into switch throws */ Py_CLEAR(kwargs); Py_CLEAR(args); } Py_DECREF(tracefunc); } Py_DECREF(origin); if (args == NULL) { /* pending exception */ result = NULL; } else { /* call g.run(*args, **kwargs) */ result = PyObject_Call(run, args, kwargs); Py_DECREF(args); Py_XDECREF(kwargs); } Py_DECREF(run); result = g_handle_exit(result); /* jump back to parent */ self->stack_start = NULL; /* dead */ for (parent = self->parent; parent != NULL; parent = parent->parent) { result = g_switch(parent, result, NULL); /* Return here means switch to parent failed, * in which case we throw *current* exception * to the next parent in chain. */ assert(result == NULL); } /* We ran out of parents, cannot continue */ PyErr_WriteUnraisable((PyObject*)self); Py_FatalError("greenlets cannot continue"); } /* back in the parent */ if (err < 0) { /* start failed badly, restore greenlet state */ self->stack_start = NULL; self->stack_stop = NULL; self->stack_prev = NULL; } return err; } /***********************************************************/ static PyObject* green_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { PyObject* o = PyBaseObject_Type.tp_new(type, ts_empty_tuple, ts_empty_dict); if (o != NULL) { if (!STATE_OK) { Py_DECREF(o); return NULL; } Py_INCREF(ts_current); ((PyGreenlet*)o)->parent = ts_current; #if GREENLET_USE_CFRAME /* The PyThreadState->cframe pointer usually points to memory on the stack, alloceted in a call into PyEval_EvalFrameDefault. Initially, before any evaluation begins, it points to the initial PyThreadState object's ``root_cframe`` object, which is statically allocated for the lifetime of the thread. A greenlet can last for longer than a call to PyEval_EvalFrameDefault, so we can't set its ``cframe`` pointer to be the current ``PyThreadState->cframe``; nor could we use one from the greenlet parent for the same reason. Yet a further no: we can't allocate one scoped to the greenlet and then destroy it when the greenlet is deallocated, because inside the interpreter the CFrame objects form a linked list, and that too can result in accessing memory beyond its dynamic lifetime (if the greenlet doesn't actually finish before it dies, its entry could still be in the list). Using the ``root_cframe`` is problematic, though, because its members are never modified by the interpreter and are set to 0, meaning that its ``use_tracing`` flag is never updated. We don't want to modify that value in the ``root_cframe`` ourself: it *shouldn't* matter much because we should probably never get back to the point where that's the only cframe on the stack; even if it did matter, the major consequence of an incorrect value for ``use_tracing`` is that if its true the interpreter does some extra work --- however, it's just good code hygiene. Our solution: before a greenlet runs, after its initial creation, it uses the ``root_cframe`` just to have something to put there. However, once the greenlet is actually switched to for the first time, ``g_initialstub`` (which doesn't actually "return" while the greenlet is running) stores a new CFrame on its local stack, and copies the appropriate values from the currently running CFrame; this is then made the CFrame for the newly-minted greenlet. ``g_initialstub`` then proceeds to call ``glet.run()``, which results in ``PyEval_...`` adding the CFrame to the list. Switches continue as normal. Finally, when the greenlet finishes, the call to ``glet.run()`` returns and the CFrame is taken out of the linked list and the stack value is now unused and free to expire. */ ((PyGreenlet*)o)->cframe = &PyThreadState_GET()->root_cframe; #endif } return o; } static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c); static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c); static int green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs) { PyObject* run = NULL; PyObject* nparent = NULL; static char* kwlist[] = {"run", "parent", 0}; if (!PyArg_ParseTupleAndKeywords( args, kwargs, "|OO:green", kwlist, &run, &nparent)) { return -1; } if (run != NULL) { if (green_setrun(self, run, NULL)) { return -1; } } if (nparent != NULL && nparent != Py_None) { return green_setparent(self, nparent, NULL); } return 0; } static int kill_greenlet(PyGreenlet* self) { /* Cannot raise an exception to kill the greenlet if it is not running in the same thread! */ if (self->run_info == PyThreadState_GET()->dict) { /* The dying greenlet cannot be a parent of ts_current because the 'parent' field chain would hold a reference */ PyObject* result; PyGreenlet* oldparent; PyGreenlet* tmp; if (!STATE_OK) { return -1; } oldparent = self->parent; self->parent = ts_current; Py_INCREF(self->parent); /* Send the greenlet a GreenletExit exception. */ PyErr_SetNone(PyExc_GreenletExit); result = g_switch(self, NULL, NULL); tmp = self->parent; self->parent = oldparent; Py_XDECREF(tmp); if (result == NULL) { return -1; } Py_DECREF(result); return 0; } else { /* Not the same thread! Temporarily save the greenlet into its thread's ts_delkey list. */ PyObject* lst; lst = PyDict_GetItem(self->run_info, ts_delkey); if (lst == NULL) { lst = PyList_New(0); if (lst == NULL || PyDict_SetItem(self->run_info, ts_delkey, lst) < 0) { return -1; } /* PyDict_SetItem now holds a strong reference. PyList_New also returned a fresh reference. We need to DECREF it now and let the dictionary keep sole ownership. Frow now on, we're working with a borrowed reference that will go away when the thread dies. */ Py_DECREF(lst); } if (PyList_Append(lst, (PyObject*)self) < 0) { return -1; } if (!STATE_OK) { /* to force ts_delkey to be reconsidered */ return -1; } return 0; } } static int green_traverse(PyGreenlet* self, visitproc visit, void* arg) { /* We must only visit referenced objects, i.e. only objects Py_INCREF'ed by this greenlet (directly or indirectly): - stack_prev is not visited: holds previous stack pointer, but it's not referenced - frames are not visited: alive greenlets are not garbage collected anyway */ Py_VISIT((PyObject*)self->parent); Py_VISIT(self->run_info); #if GREENLET_PY37 Py_VISIT(self->context); #endif #if GREENLET_PY37 Py_VISIT(self->exc_state.exc_type); Py_VISIT(self->exc_state.exc_value); Py_VISIT(self->exc_state.exc_traceback); #else Py_VISIT(self->exc_type); Py_VISIT(self->exc_value); Py_VISIT(self->exc_traceback); #endif Py_VISIT(self->dict); return 0; } static int green_is_gc(PyGreenlet* self) { /* Main greenlet can be garbage collected since it can only become unreachable if the underlying thread exited. Active greenlet cannot be garbage collected, however. */ if (PyGreenlet_MAIN(self) || !PyGreenlet_ACTIVE(self)) { return 1; } return 0; } static int green_clear(PyGreenlet* self) { /* Greenlet is only cleared if it is about to be collected. Since active greenlets are not garbage collectable, we can be sure that, even if they are deallocated during clear, nothing they reference is in unreachable or finalizers, so even if it switches we are relatively safe. */ Py_CLEAR(self->parent); Py_CLEAR(self->run_info); #if GREENLET_PY37 Py_CLEAR(self->context); #endif #if GREENLET_PY37 Py_CLEAR(self->exc_state.exc_type); Py_CLEAR(self->exc_state.exc_value); Py_CLEAR(self->exc_state.exc_traceback); #else Py_CLEAR(self->exc_type); Py_CLEAR(self->exc_value); Py_CLEAR(self->exc_traceback); #endif Py_CLEAR(self->dict); return 0; } static void green_dealloc(PyGreenlet* self) { PyObject *error_type, *error_value, *error_traceback; Py_ssize_t refcnt; PyObject_GC_UnTrack(self); if (PyGreenlet_ACTIVE(self) && self->run_info != NULL && !PyGreenlet_MAIN(self)) { /* Hacks hacks hacks copied from instance_dealloc() */ /* Temporarily resurrect the greenlet. */ assert(Py_REFCNT(self) == 0); Py_SET_REFCNT(self, 1); /* Save the current exception, if any. */ PyErr_Fetch(&error_type, &error_value, &error_traceback); if (kill_greenlet(self) < 0) { PyErr_WriteUnraisable((PyObject*)self); /* XXX what else should we do? */ } /* Check for no resurrection must be done while we keep * our internal reference, otherwise PyFile_WriteObject * causes recursion if using Py_INCREF/Py_DECREF */ if (Py_REFCNT(self) == 1 && PyGreenlet_ACTIVE(self)) { /* Not resurrected, but still not dead! XXX what else should we do? we complain. */ PyObject* f = PySys_GetObject("stderr"); Py_INCREF(self); /* leak! */ if (f != NULL) { PyFile_WriteString("GreenletExit did not kill ", f); PyFile_WriteObject((PyObject*)self, f, 0); PyFile_WriteString("\n", f); } } /* Restore the saved exception. */ PyErr_Restore(error_type, error_value, error_traceback); /* Undo the temporary resurrection; can't use DECREF here, * it would cause a recursive call. */ assert(Py_REFCNT(self) > 0); refcnt = Py_REFCNT(self) - 1; Py_SET_REFCNT(self, refcnt); if (refcnt != 0) { /* Resurrected! */ _Py_NewReference((PyObject*)self); Py_SET_REFCNT(self, refcnt); /* Better to use tp_finalizer slot (PEP 442) * and call ``PyObject_CallFinalizerFromDealloc``, * but that's only supported in Python 3.4+; see * Modules/_io/iobase.c for an example. * * The following approach is copied from iobase.c in CPython 2.7. * (along with much of this function in general). Here's their * comment: * * When called from a heap type's dealloc, the type will be * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ if (PyType_HasFeature(Py_TYPE(self), Py_TPFLAGS_HEAPTYPE)) { Py_INCREF(Py_TYPE(self)); } PyObject_GC_Track((PyObject*)self); _Py_DEC_REFTOTAL; #ifdef COUNT_ALLOCS --Py_TYPE(self)->tp_frees; --Py_TYPE(self)->tp_allocs; #endif /* COUNT_ALLOCS */ return; } } if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject*)self); } Py_CLEAR(self->parent); Py_CLEAR(self->run_info); #if GREENLET_PY37 Py_CLEAR(self->context); #endif #if GREENLET_PY37 Py_CLEAR(self->exc_state.exc_type); Py_CLEAR(self->exc_state.exc_value); Py_CLEAR(self->exc_state.exc_traceback); #else Py_CLEAR(self->exc_type); Py_CLEAR(self->exc_value); Py_CLEAR(self->exc_traceback); #endif Py_CLEAR(self->dict); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* single_result(PyObject* results) { if (results != NULL && PyTuple_Check(results) && PyTuple_GET_SIZE(results) == 1) { PyObject* result = PyTuple_GET_ITEM(results, 0); Py_INCREF(result); Py_DECREF(results); return result; } else { return results; } } static PyObject* throw_greenlet(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) { /* Note: _consumes_ a reference to typ, val, tb */ PyObject* result = NULL; PyErr_Restore(typ, val, tb); if (PyGreenlet_STARTED(self) && !PyGreenlet_ACTIVE(self)) { /* dead greenlet: turn GreenletExit into a regular return */ result = g_handle_exit(result); } return single_result(g_switch(self, result, NULL)); } PyDoc_STRVAR( green_switch_doc, "switch(*args, **kwargs)\n" "\n" "Switch execution to this greenlet.\n" "\n" "If this greenlet has never been run, then this greenlet\n" "will be switched to using the body of ``self.run(*args, **kwargs)``.\n" "\n" "If the greenlet is active (has been run, but was switch()'ed\n" "out before leaving its run function), then this greenlet will\n" "be resumed and the return value to its switch call will be\n" "None if no arguments are given, the given argument if one\n" "argument is given, or the args tuple and keyword args dict if\n" "multiple arguments are given.\n" "\n" "If the greenlet is dead, or is the current greenlet then this\n" "function will simply return the arguments using the same rules as\n" "above.\n"); static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) { Py_INCREF(args); Py_XINCREF(kwargs); return single_result(g_switch(self, args, kwargs)); } PyDoc_STRVAR( green_throw_doc, "Switches execution to this greenlet, but immediately raises the\n" "given exception in this greenlet. If no argument is provided, the " "exception\n" "defaults to `greenlet.GreenletExit`. The normal exception\n" "propagation rules apply, as described for `switch`. Note that calling " "this\n" "method is almost equivalent to the following::\n" "\n" " def raiser():\n" " raise typ, val, tb\n" " g_raiser = greenlet(raiser, parent=g)\n" " g_raiser.switch()\n" "\n" "except that this trick does not work for the\n" "`greenlet.GreenletExit` exception, which would not propagate\n" "from ``g_raiser`` to ``g``.\n"); static PyObject* green_throw(PyGreenlet* self, PyObject* args) { PyObject* typ = PyExc_GreenletExit; PyObject* val = NULL; PyObject* tb = NULL; if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) { return NULL; } /* First, check the traceback argument, replacing None, with NULL */ if (tb == Py_None) { tb = NULL; } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback object"); return NULL; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); if (PyExceptionClass_Check(typ)) { PyErr_NormalizeException(&typ, &val, &tb); } else if (PyExceptionInstance_Check(typ)) { /* Raising an instance. The value should be a dummy. */ if (val && val != Py_None) { PyErr_SetString( PyExc_TypeError, "instance exception may not have a separate value"); goto failed_throw; } else { /* Normalize to raise , */ Py_XDECREF(val); val = typ; typ = PyExceptionInstance_Class(typ); Py_INCREF(typ); } } else { /* Not something you can raise. throw() fails. */ PyErr_Format(PyExc_TypeError, "exceptions must be classes, or instances, not %s", Py_TYPE(typ)->tp_name); goto failed_throw; } return throw_greenlet(self, typ, val, tb); failed_throw: /* Didn't use our arguments, so restore their original refcounts */ Py_DECREF(typ); Py_XDECREF(val); Py_XDECREF(tb); return NULL; } static int green_bool(PyGreenlet* self) { return PyGreenlet_ACTIVE(self); } static PyObject* green_getdict(PyGreenlet* self, void* c) { if (self->dict == NULL) { self->dict = PyDict_New(); if (self->dict == NULL) { return NULL; } } Py_INCREF(self->dict); return self->dict; } static int green_setdict(PyGreenlet* self, PyObject* val, void* c) { PyObject* tmp; if (val == NULL) { PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted"); return -1; } if (!PyDict_Check(val)) { PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary"); return -1; } tmp = self->dict; Py_INCREF(val); self->dict = val; Py_XDECREF(tmp); return 0; } static int _green_not_dead(PyGreenlet* self) { return PyGreenlet_ACTIVE(self) || !PyGreenlet_STARTED(self); } static PyObject* green_getdead(PyGreenlet* self, void* c) { if (_green_not_dead(self)) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; } } static PyObject* green_get_stack_saved(PyGreenlet* self, void* c) { return PyLong_FromSsize_t(self->stack_saved); } static PyObject* green_getrun(PyGreenlet* self, void* c) { if (PyGreenlet_STARTED(self) || self->run_info == NULL) { PyErr_SetString(PyExc_AttributeError, "run"); return NULL; } Py_INCREF(self->run_info); return self->run_info; } static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c) { PyObject* o; if (PyGreenlet_STARTED(self)) { PyErr_SetString(PyExc_AttributeError, "run cannot be set " "after the start of the greenlet"); return -1; } o = self->run_info; self->run_info = nrun; Py_XINCREF(nrun); Py_XDECREF(o); return 0; } static PyObject* green_getparent(PyGreenlet* self, void* c) { PyObject* result = self->parent ? (PyObject*)self->parent : Py_None; Py_INCREF(result); return result; } static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c) { PyGreenlet* p; PyObject* run_info = NULL; if (nparent == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete attribute"); return -1; } if (!PyGreenlet_Check(nparent)) { PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); return -1; } for (p = (PyGreenlet*)nparent; p; p = p->parent) { if (p == self) { PyErr_SetString(PyExc_ValueError, "cyclic parent chain"); return -1; } run_info = PyGreenlet_ACTIVE(p) ? p->run_info : NULL; } if (run_info == NULL) { PyErr_SetString(PyExc_ValueError, "parent must not be garbage collected"); return -1; } if (PyGreenlet_STARTED(self) && self->run_info != run_info) { PyErr_SetString(PyExc_ValueError, "parent cannot be on a different thread"); return -1; } p = self->parent; self->parent = (PyGreenlet*)nparent; Py_INCREF(nparent); Py_XDECREF(p); return 0; } #ifdef Py_CONTEXT_H # define GREENLET_NO_CONTEXTVARS_REASON "This build of greenlet" #else # define GREENLET_NO_CONTEXTVARS_REASON "This Python interpreter" #endif static PyObject* green_getcontext(PyGreenlet* self, void* c) { #if GREENLET_PY37 PyThreadState* tstate = PyThreadState_GET(); PyObject* result; if (!STATE_OK) { return NULL; } if (PyGreenlet_ACTIVE(self) && self->top_frame == NULL) { /* Currently running greenlet: context is stored in the thread state, not the greenlet object. */ if (self == ts_current) { result = tstate->context; } else { PyErr_SetString(PyExc_ValueError, "cannot get context of a " "greenlet that is running in a different thread"); return NULL; } } else { /* Greenlet is not running: just return context. */ result = self->context; } if (result == NULL) { result = Py_None; } Py_INCREF(result); return result; #else PyErr_SetString(PyExc_AttributeError, GREENLET_NO_CONTEXTVARS_REASON " does not support context variables"); return NULL; #endif } static int green_setcontext(PyGreenlet* self, PyObject* nctx, void* c) { #if GREENLET_PY37 PyThreadState* tstate; PyObject* octx = NULL; if (!STATE_OK) { return -1; } if (nctx == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete attribute"); return -1; } if (nctx == Py_None) { /* "Empty context" is stored as NULL, not None. */ nctx = NULL; } else if (!PyContext_CheckExact(nctx)) { PyErr_SetString(PyExc_TypeError, "greenlet context must be a " "contextvars.Context or None"); return -1; } tstate = PyThreadState_GET(); if (PyGreenlet_ACTIVE(self) && self->top_frame == NULL) { /* Currently running greenlet: context is stored in the thread state, not the greenlet object. */ if (self == ts_current) { octx = tstate->context; tstate->context = nctx; tstate->context_ver++; Py_XINCREF(nctx); } else { PyErr_SetString(PyExc_ValueError, "cannot set context of a " "greenlet that is running in a different thread"); return -1; } } else { /* Greenlet is not running: just set context. */ octx = self->context; self->context = nctx; Py_XINCREF(nctx); } Py_XDECREF(octx); return 0; #else PyErr_SetString(PyExc_AttributeError, GREENLET_NO_CONTEXTVARS_REASON " does not support context variables"); return -1; #endif } #undef GREENLET_NO_CONTEXTVARS_REASON static PyObject* green_getframe(PyGreenlet* self, void* c) { PyObject* result = self->top_frame ? (PyObject*)self->top_frame : Py_None; Py_INCREF(result); return result; } static PyObject* green_getstate(PyGreenlet* self) { PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", Py_TYPE(self)->tp_name); return NULL; } static PyObject* green_repr(PyGreenlet* self) { /* Return a string like The handling of greenlets across threads is not super good. We mostly use the internal definitions of these terms, but they generally should make sense to users as well. */ PyObject* result; int never_started = !PyGreenlet_STARTED(self) && !PyGreenlet_ACTIVE(self); if (!STATE_OK) { return NULL; } #if PY_MAJOR_VERSION >= 3 # define GNative_FromFormat PyUnicode_FromFormat #else # define GNative_FromFormat PyString_FromFormat #endif if (_green_not_dead(self)) { /* XXX: The otid= is almost useless becasue you can't correlate it to any thread identifier exposed to Python. We could use PyThreadState_GET()->thread_id, but we'd need to save that in the greenlet, or save the whole PyThreadState object itself. As it stands, its only useful for identifying greenlets from the same thread. */ result = GNative_FromFormat( "<%s object at %p (otid=%p)%s%s%s%s>", Py_TYPE(self)->tp_name, self, self->run_info, ts_current == self ? " current" : (PyGreenlet_STARTED(self) ? " suspended" : ""), PyGreenlet_ACTIVE(self) ? " active" : "", never_started ? " pending" : " started", PyGreenlet_MAIN(self) ? " main" : "" ); } else { /* main greenlets never really appear dead. */ result = GNative_FromFormat( "<%s object at %p (otid=%p) dead>", Py_TYPE(self)->tp_name, self, self->run_info ); } #undef GNative_FromFormat return result; } /***************************************************************************** * C interface * * These are exported using the CObject API */ static PyGreenlet* PyGreenlet_GetCurrent(void) { if (!STATE_OK) { return NULL; } Py_INCREF(ts_current); return ts_current; } static int PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent) { if (!PyGreenlet_Check(g)) { PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); return -1; } return green_setparent((PyGreenlet*)g, (PyObject*)nparent, NULL); } static PyGreenlet* PyGreenlet_New(PyObject* run, PyGreenlet* parent) { /* XXX: Why doesn't this call green_new()? There's some duplicate code. */ PyGreenlet* g = NULL; g = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0); if (g == NULL) { return NULL; } if (run != NULL) { Py_INCREF(run); g->run_info = run; } if (parent != NULL) { if (PyGreenlet_SetParent(g, parent)) { Py_DECREF(g); return NULL; } } else { if ((g->parent = PyGreenlet_GetCurrent()) == NULL) { Py_DECREF(g); return NULL; } } #if GREENLET_USE_CFRAME g->cframe = &PyThreadState_GET()->root_cframe; #endif return g; } static PyObject* PyGreenlet_Switch(PyGreenlet* g, PyObject* args, PyObject* kwargs) { PyGreenlet* self = (PyGreenlet*)g; if (!PyGreenlet_Check(self)) { PyErr_BadArgument(); return NULL; } if (args == NULL) { args = Py_BuildValue("()"); } else { Py_INCREF(args); } if (kwargs != NULL && PyDict_Check(kwargs)) { Py_INCREF(kwargs); } else { kwargs = NULL; } return single_result(g_switch(self, args, kwargs)); } static PyObject* PyGreenlet_Throw(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) { if (!PyGreenlet_Check(self)) { PyErr_BadArgument(); return NULL; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); return throw_greenlet(self, typ, val, tb); } /** End C API ****************************************************************/ static PyMethodDef green_methods[] = { {"switch", (PyCFunction)green_switch, METH_VARARGS | METH_KEYWORDS, green_switch_doc}, {"throw", (PyCFunction)green_throw, METH_VARARGS, green_throw_doc}, {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; static PyGetSetDef green_getsets[] = { {"__dict__", (getter)green_getdict, (setter)green_setdict, /*XXX*/ NULL}, {"run", (getter)green_getrun, (setter)green_setrun, /*XXX*/ NULL}, {"parent", (getter)green_getparent, (setter)green_setparent, /*XXX*/ NULL}, {"gr_frame", (getter)green_getframe, NULL, /*XXX*/ NULL}, {"gr_context", (getter)green_getcontext, (setter)green_setcontext, /*XXX*/ NULL}, {"dead", (getter)green_getdead, NULL, /*XXX*/ NULL}, {"_stack_saved", (getter)green_get_stack_saved, NULL, /*XXX*/ NULL}, {NULL}}; static PyNumberMethods green_as_number = { NULL, /* nb_add */ NULL, /* nb_subtract */ NULL, /* nb_multiply */ #if PY_MAJOR_VERSION < 3 NULL, /* nb_divide */ #endif NULL, /* nb_remainder */ NULL, /* nb_divmod */ NULL, /* nb_power */ NULL, /* nb_negative */ NULL, /* nb_positive */ NULL, /* nb_absolute */ (inquiry)green_bool, /* nb_bool */ }; PyTypeObject PyGreenlet_Type = { PyVarObject_HEAD_INIT(NULL, 0) "greenlet.greenlet", /* tp_name */ sizeof(PyGreenlet), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)green_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)green_repr, /* tp_repr */ &green_as_number, /* tp_as _number*/ 0, /* tp_as _sequence*/ 0, /* tp_as _mapping*/ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | GREENLET_GC_FLAGS, /* tp_flags */ "greenlet(run=None, parent=None) -> greenlet\n\n" "Creates a new greenlet object (without running it).\n\n" " - *run* -- The callable to invoke.\n" " - *parent* -- The parent greenlet. The default is the current " "greenlet.", /* tp_doc */ (traverseproc)GREENLET_tp_traverse, /* tp_traverse */ (inquiry)GREENLET_tp_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ green_methods, /* tp_methods */ 0, /* tp_members */ green_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(PyGreenlet, dict), /* tp_dictoffset */ (initproc)green_init, /* tp_init */ GREENLET_tp_alloc, /* tp_alloc */ green_new, /* tp_new */ GREENLET_tp_free, /* tp_free */ (inquiry)GREENLET_tp_is_gc, /* tp_is_gc */ }; PyDoc_STRVAR(mod_getcurrent_doc, "getcurrent() -> greenlet\n" "\n" "Returns the current greenlet (i.e. the one which called this " "function).\n"); static PyObject* mod_getcurrent(PyObject* self) { if (!STATE_OK) { return NULL; } Py_INCREF(ts_current); return (PyObject*)ts_current; } PyDoc_STRVAR(mod_settrace_doc, "settrace(callback) -> object\n" "\n" "Sets a new tracing function and returns the previous one.\n"); static PyObject* mod_settrace(PyObject* self, PyObject* args) { int err; PyObject* previous; PyObject* tracefunc; PyGreenlet* current; if (!PyArg_ParseTuple(args, "O", &tracefunc)) { return NULL; } if (!STATE_OK) { return NULL; } current = ts_current; previous = PyDict_GetItem(current->run_info, ts_tracekey); if (previous == NULL) { previous = Py_None; } Py_INCREF(previous); if (tracefunc == Py_None) { err = previous != Py_None ? PyDict_DelItem(current->run_info, ts_tracekey) : 0; } else { err = PyDict_SetItem(current->run_info, ts_tracekey, tracefunc); } if (err < 0) { Py_CLEAR(previous); } return previous; } PyDoc_STRVAR(mod_gettrace_doc, "gettrace() -> object\n" "\n" "Returns the currently set tracing function, or None.\n"); static PyObject* mod_gettrace(PyObject* self) { PyObject* tracefunc; if (!STATE_OK) { return NULL; } tracefunc = PyDict_GetItem(ts_current->run_info, ts_tracekey); if (tracefunc == NULL) { tracefunc = Py_None; } Py_INCREF(tracefunc); return tracefunc; } static PyMethodDef GreenMethods[] = { {"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, mod_getcurrent_doc}, {"settrace", (PyCFunction)mod_settrace, METH_VARARGS, mod_settrace_doc}, {"gettrace", (PyCFunction)mod_gettrace, METH_NOARGS, mod_gettrace_doc}, {NULL, NULL} /* Sentinel */ }; static char* copy_on_greentype[] = { "getcurrent", "error", "GreenletExit", "settrace", "gettrace", NULL}; #if PY_MAJOR_VERSION >= 3 # define INITERROR return NULL static struct PyModuleDef greenlet_module_def = { PyModuleDef_HEAD_INIT, "greenlet._greenlet", NULL, -1, GreenMethods, }; PyMODINIT_FUNC PyInit__greenlet(void) #else # define INITERROR return PyMODINIT_FUNC init_greenlet(void) #endif { PyObject* m = NULL; char** p = NULL; PyObject* c_api_object; static void* _PyGreenlet_API[PyGreenlet_API_pointers]; GREENLET_NOINLINE_INIT(); #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&greenlet_module_def); #else m = Py_InitModule("greenlet._greenlet", GreenMethods); #endif if (m == NULL) { INITERROR; } #if PY_MAJOR_VERSION >= 3 # define Greenlet_Intern PyUnicode_InternFromString #else # define Greenlet_Intern PyString_InternFromString #endif ts_curkey = Greenlet_Intern("__greenlet_ts_curkey"); ts_delkey = Greenlet_Intern("__greenlet_ts_delkey"); ts_tracekey = Greenlet_Intern("__greenlet_ts_tracekey"); ts_event_switch = Greenlet_Intern("switch"); ts_event_throw = Greenlet_Intern("throw"); #undef Greenlet_Intern if (ts_curkey == NULL || ts_delkey == NULL) { INITERROR; } if (PyType_Ready(&PyGreenlet_Type) < 0) { INITERROR; } PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL); if (PyExc_GreenletError == NULL) { INITERROR; } PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit", PyExc_BaseException, NULL); if (PyExc_GreenletExit == NULL) { INITERROR; } ts_empty_tuple = PyTuple_New(0); if (ts_empty_tuple == NULL) { INITERROR; } ts_empty_dict = PyDict_New(); if (ts_empty_dict == NULL) { INITERROR; } ts_current = green_create_main(); if (ts_current == NULL) { INITERROR; } Py_INCREF(&PyGreenlet_Type); PyModule_AddObject(m, "greenlet", (PyObject*)&PyGreenlet_Type); Py_INCREF(PyExc_GreenletError); PyModule_AddObject(m, "error", PyExc_GreenletError); Py_INCREF(PyExc_GreenletExit); PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit); PyModule_AddObject(m, "GREENLET_USE_GC", PyBool_FromLong(1)); PyModule_AddObject(m, "GREENLET_USE_TRACING", PyBool_FromLong(1)); PyModule_AddObject( m, "GREENLET_USE_CONTEXT_VARS", PyBool_FromLong(GREENLET_PY37)); /* also publish module-level data as attributes of the greentype. */ /* XXX: Why? */ for (p = copy_on_greentype; *p; p++) { PyObject* o = PyObject_GetAttrString(m, *p); if (!o) { continue; } PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o); Py_DECREF(o); } /* * Expose C API */ /* types */ _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type; /* exceptions */ _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)PyExc_GreenletError; _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)PyExc_GreenletExit; /* methods */ _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New; _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent; _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw; _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch; _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent; /* XXX: Note that our module name is ``greenlet._greenlet``, but for backwards compatibility with existing C code, we need the _C_API to be directly in greenlet. */ c_api_object = PyCapsule_New((void*)_PyGreenlet_API, "greenlet._C_API", NULL); if (c_api_object != NULL) { PyModule_AddObject(m, "_C_API", c_api_object); } #if PY_MAJOR_VERSION >= 3 return m; #endif } #ifdef __clang__ # pragma clang diagnostic pop #endif python-greenlet-1.1.2/src/greenlet/greenlet.h000066400000000000000000000102251414024117000212030ustar00rootroot00000000000000/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ /* Greenlet object interface */ #ifndef Py_GREENLETOBJECT_H #define Py_GREENLETOBJECT_H #include #ifdef __cplusplus extern "C" { #endif /* This is deprecated and undocumented. It does not change. */ #define GREENLET_VERSION "1.0.0" typedef struct _greenlet { PyObject_HEAD char* stack_start; char* stack_stop; char* stack_copy; intptr_t stack_saved; struct _greenlet* stack_prev; struct _greenlet* parent; PyObject* run_info; struct _frame* top_frame; int recursion_depth; PyObject* weakreflist; #if PY_VERSION_HEX >= 0x030700A3 _PyErr_StackItem* exc_info; _PyErr_StackItem exc_state; #else PyObject* exc_type; PyObject* exc_value; PyObject* exc_traceback; #endif PyObject* dict; #if PY_VERSION_HEX >= 0x030700A3 PyObject* context; #endif #if PY_VERSION_HEX >= 0x30A00B1 CFrame* cframe; #endif } PyGreenlet; #define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) #define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1) #define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) #define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) #define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) /* C API functions */ /* Total number of symbols that are exported */ #define PyGreenlet_API_pointers 8 #define PyGreenlet_Type_NUM 0 #define PyExc_GreenletError_NUM 1 #define PyExc_GreenletExit_NUM 2 #define PyGreenlet_New_NUM 3 #define PyGreenlet_GetCurrent_NUM 4 #define PyGreenlet_Throw_NUM 5 #define PyGreenlet_Switch_NUM 6 #define PyGreenlet_SetParent_NUM 7 #ifndef GREENLET_MODULE /* This section is used by modules that uses the greenlet C API */ static void** _PyGreenlet_API = NULL; # define PyGreenlet_Type \ (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) # define PyExc_GreenletError \ ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) # define PyExc_GreenletExit \ ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) /* * PyGreenlet_New(PyObject *args) * * greenlet.greenlet(run, parent=None) */ # define PyGreenlet_New \ (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ _PyGreenlet_API[PyGreenlet_New_NUM]) /* * PyGreenlet_GetCurrent(void) * * greenlet.getcurrent() */ # define PyGreenlet_GetCurrent \ (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) /* * PyGreenlet_Throw( * PyGreenlet *greenlet, * PyObject *typ, * PyObject *val, * PyObject *tb) * * g.throw(...) */ # define PyGreenlet_Throw \ (*(PyObject * (*)(PyGreenlet * self, \ PyObject * typ, \ PyObject * val, \ PyObject * tb)) \ _PyGreenlet_API[PyGreenlet_Throw_NUM]) /* * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) * * g.switch(*args, **kwargs) */ # define PyGreenlet_Switch \ (*(PyObject * \ (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ _PyGreenlet_API[PyGreenlet_Switch_NUM]) /* * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) * * g.parent = new_parent */ # define PyGreenlet_SetParent \ (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ _PyGreenlet_API[PyGreenlet_SetParent_NUM]) /* Macro that imports greenlet and initializes C API */ /* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we keep the older definition to be sure older code that might have a copy of the header still works. */ # define PyGreenlet_Import() \ { \ _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ } #endif /* GREENLET_MODULE */ #ifdef __cplusplus } #endif #endif /* !Py_GREENLETOBJECT_H */ python-greenlet-1.1.2/src/greenlet/platform/000077500000000000000000000000001414024117000210515ustar00rootroot00000000000000python-greenlet-1.1.2/src/greenlet/platform/setup_switch_x64_masm.cmd000066400000000000000000000002171414024117000257750ustar00rootroot00000000000000call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64 ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm python-greenlet-1.1.2/src/greenlet/platform/switch_aarch64_gcc.h000066400000000000000000000045211414024117000246510ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall * 13-Apr-13 Add support for strange GCC caller-save decisions * 08-Apr-13 File creation. Michael Matz * * NOTES * * Simply save all callee saved registers * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \ "x27", "x28", "x30" /* aka lr */, \ "v8", "v9", "v10", "v11", \ "v12", "v13", "v14", "v15" static int slp_switch(void) { int err; void *fp; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("str x29, %0" : "=m"(fp) : : ); __asm__ ("mov %0, sp" : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add sp,sp,%0\n" "add x29,x29,%0\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); /* SLP_SAVE_STATE macro contains some return statements (of -1 and 1). It falls through only when the return value of slp_save_state() is zero, which is placed in x0. In that case we (slp_switch) also want to return zero (also in x0 of course). Now, some GCC versions (seen with 4.8) think it's a good idea to save/restore x0 around the call to slp_restore_state(), instead of simply zeroing it at the return below. But slp_restore_state writes random values to the stack slot used for this save/restore (from when it once was saved above in SLP_SAVE_STATE, when it was still uninitialized), so "restoring" that precious zero actually makes us return random values. There are some ways to make GCC not use that zero value in the normal return path (e.g. making err volatile, but that costs a little stack space), and the simplest is to call a function that returns an unknown value (which happens to be zero), so the saved/restored value is unused. */ __asm__ volatile ("mov %0, #0" : "=r" (err)); } __asm__ volatile ("ldr x29, %0" : : "m" (fp) :); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_alpha_unix.h000066400000000000000000000012611414024117000247330ustar00rootroot00000000000000#define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \ "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9" static int slp_switch(void) { register int ret; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mov $30, %0" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addq $30, %0, $30\n\t" : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mov $31, %0" : "=r" (ret) : ); return ret; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_amd64_unix.h000066400000000000000000000050621414024117000245640ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 3-May-13 Ralf Schmitt * Add support for strange GCC caller-save decisions * (ported from switch_aarch64_gcc.h) * 18-Aug-11 Alexey Borzenkov * Correctly save rbp, csr and cw * 01-Apr-04 Hye-Shik Chang * Ported from i386 to amd64. * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for spark * 31-Avr-02 Armin Rigo * Added ebx, esi and edi register-saves. * 01-Mar-02 Samual M. Rushing * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL /* #define STACK_MAGIC 3 */ /* the above works fine with gcc 2.96, but 2.95.3 wants this */ #define STACK_MAGIC 0 #define REGS_TO_SAVE "r12", "r13", "r14", "r15" static int slp_switch(void) { int err; void* rbp; void* rbx; unsigned int csr; unsigned short cw; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("stmxcsr %0" : "=m" (csr)); __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp)); __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx)); __asm__ ("movq %%rsp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addq %0, %%rsp\n" "addq %0, %%rbp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err)); } __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx)); __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp)); __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_arm32_gcc.h000066400000000000000000000046701414024117000243520ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I * read that these do not need to be saved. Also added notes and * errors related to the frame pointer. Richard Tew. * * NOTES * * It is not possible to detect if fp is used or not, so the supplied * switch function needs to support it, so that you can remove it if * it does not apply to you. * * POSSIBLE ERRORS * * "fp cannot be used in asm here" * * - Try commenting out "fp" in REGS_TO_SAVE. * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REG_SP "sp" #define REG_SPSP "sp,sp" #ifdef __thumb__ #define REG_FP "r7" #define REG_FPFP "r7,r7" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr" #else #define REG_FP "fp" #define REG_FPFP "fp,fp" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr" #endif #if defined(__SOFTFP__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL #elif defined(__VFP_FP__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ "d12", "d13", "d14", "d15" #elif defined(__MAVERICK__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \ "mvf8", "mvf9", "mvf10", "mvf11", \ "mvf12", "mvf13", "mvf14", "mvf15" #else #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7" #endif static int #ifdef __GNUC__ __attribute__((optimize("no-omit-frame-pointer"))) #endif slp_switch(void) { void *fp; register int *stackref, stsizediff; int result; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0"); __asm__ ("mov %0," REG_SP : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add " REG_SPSP ",%0\n" "add " REG_FPFP ",%0\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0"); __asm__ volatile ("" : : : REGS_TO_SAVE); return result; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_arm32_ios.h000066400000000000000000000035551414024117000244110ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 31-May-15 iOS support. Ported from arm32. Proton * * NOTES * * It is not possible to detect if fp is used or not, so the supplied * switch function needs to support it, so that you can remove it if * it does not apply to you. * * POSSIBLE ERRORS * * "fp cannot be used in asm here" * * - Try commenting out "fp" in REGS_TO_SAVE. * */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REG_SP "sp" #define REG_SPSP "sp,sp" #define REG_FP "r7" #define REG_FPFP "r7,r7" #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr" #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ "d12", "d13", "d14", "d15" static int #ifdef __GNUC__ __attribute__((optimize("no-omit-frame-pointer"))) #endif slp_switch(void) { void *fp; register int *stackref, stsizediff, result; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("str " REG_FP ",%0" : "=m" (fp)); __asm__ ("mov %0," REG_SP : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add " REG_SPSP ",%0\n" "add " REG_FPFP ",%0\n" : : "r" (stsizediff) : REGS_TO_SAVE /* Clobber registers, force compiler to * recalculate address of void *fp from REG_SP or REG_FP */ ); SLP_RESTORE_STATE(); } __asm__ volatile ( "ldr " REG_FP ", %1\n\t" "mov %0, #0" : "=r" (result) : "m" (fp) : REGS_TO_SAVE /* Force compiler to restore saved registers after this */ ); return result; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_csky_gcc.h000066400000000000000000000024741414024117000243770ustar00rootroot00000000000000#ifdef SLP_EVAL #define STACK_MAGIC 0 #define REG_FP "r8" #ifdef __CSKYABIV2__ #define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r9", "r10", "r11", "r15",\ "r16", "r17", "r18", "r19", "r20", "r21", "r22",\ "r23", "r24", "r25" #if defined (__CSKY_HARD_FLOAT__) || (__CSKY_VDSP__) #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "vr8", "vr9", "vr10", "vr11", "vr12",\ "vr13", "vr14", "vr15" #else #define REGS_TO_SAVE REGS_TO_SAVE_GENERAL #endif #else #define REGS_TO_SAVE "r9", "r10", "r11", "r12", "r13", "r15" #endif static int #ifdef __GNUC__ __attribute__((optimize("no-omit-frame-pointer"))) #endif slp_switch(void) { register int *stackref, stsizediff; int result; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mov %0, sp" : "=r" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addu sp,%0\n" "addu "REG_FP",%0\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("movi %0, 0" : "=r" (result)); __asm__ volatile ("" : : : REGS_TO_SAVE); return result; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_m68k_gcc.h000066400000000000000000000016401414024117000242050ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 2014-01-06 Andreas Schwab * File created. */ #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \ "%a2", "%a3", "%a4" static int slp_switch(void) { int err; int *stackref, stsizediff; void *fp, *a5; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("move.l %%fp, %0" : "=m"(fp)); __asm__ volatile ("move.l %%a5, %0" : "=m"(a5)); __asm__ ("move.l %%sp, %0" : "=r"(stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff)); SLP_RESTORE_STATE(); __asm__ volatile ("clr.l %0" : "=g" (err)); } __asm__ volatile ("move.l %0, %%a5" : : "m"(a5)); __asm__ volatile ("move.l %0, %%fp" : : "m"(fp)); __asm__ volatile ("" : : : REGS_TO_SAVE); return err; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_mips_unix.h000066400000000000000000000026441414024117000246240ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 20-Sep-14 Matt Madison * Re-code the saving of the gp register for MIPS64. * 05-Jan-08 Thiemo Seufer * Ported from ppc. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ "$23", "$30" static int slp_switch(void) { register int err; register int *stackref, stsizediff; #ifdef __mips64 uint64_t gpsave; #endif __asm__ __volatile__ ("" : : : REGS_TO_SAVE); #ifdef __mips64 __asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : ); #endif __asm__ ("move %0, $29" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ __volatile__ ( #ifdef __mips64 "daddu $29, %0\n" #else "addu $29, %0\n" #endif : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } #ifdef __mips64 __asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : ); #endif __asm__ __volatile__ ("" : : : REGS_TO_SAVE); __asm__ __volatile__ ("move %0, $0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_ppc64_aix.h000066400000000000000000000074461414024117000244130ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 16-Oct-20 Jesse Gorzinski * Copied from Linux PPC64 implementation * 04-Sep-18 Alexey Borzenkov * Workaround a gcc bug using manual save/restore of r30 * 21-Mar-18 Tulio Magno Quites Machado Filho * Added r30 to the list of saved registers in order to fully comply with * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this * register as a nonvolatile register used for local variables. * 21-Mar-18 Laszlo Boszormenyi * Save r2 (TOC pointer) manually. * 10-Dec-13 Ulrich Weigand * Support ELFv2 ABI. Save float/vector registers. * 09-Mar-12 Michael Ellerman * 64-bit implementation, copied from 32-bit. * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. * 31-Jul-12 Trevor Bowen * Changed memory constraints to register only. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 6 #if defined(__ALTIVEC__) #define ALTIVEC_REGS \ "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ "v28", "v29", "v30", "v31", #else #define ALTIVEC_REGS #endif #define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "r31", \ "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ "fr30", "fr31", \ ALTIVEC_REGS \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register long *stackref, stsizediff; void * toc; void * r30; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("std 2, %0" : "=m" (toc)); __asm__ volatile ("std 30, %0" : "=m" (r30)); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("ld 30, %0" : : "m" (r30)); __asm__ volatile ("ld 2, %0" : : "m" (toc)); __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_ppc64_linux.h000066400000000000000000000073711414024117000247660ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 04-Sep-18 Alexey Borzenkov * Workaround a gcc bug using manual save/restore of r30 * 21-Mar-18 Tulio Magno Quites Machado Filho * Added r30 to the list of saved registers in order to fully comply with * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this * register as a nonvolatile register used for local variables. * 21-Mar-18 Laszlo Boszormenyi * Save r2 (TOC pointer) manually. * 10-Dec-13 Ulrich Weigand * Support ELFv2 ABI. Save float/vector registers. * 09-Mar-12 Michael Ellerman * 64-bit implementation, copied from 32-bit. * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. * 31-Jul-12 Trevor Bowen * Changed memory constraints to register only. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #if _CALL_ELF == 2 #define STACK_MAGIC 4 #else #define STACK_MAGIC 6 #endif #if defined(__ALTIVEC__) #define ALTIVEC_REGS \ "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ "v28", "v29", "v30", "v31", #else #define ALTIVEC_REGS #endif #define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "r31", \ "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ "fr30", "fr31", \ ALTIVEC_REGS \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register long *stackref, stsizediff; void * toc; void * r30; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("std 2, %0" : "=m" (toc)); __asm__ volatile ("std 30, %0" : "=m" (r30)); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("ld 30, %0" : : "m" (r30)); __asm__ volatile ("ld 2, %0" : : "m" (toc)); __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_ppc_aix.h000066400000000000000000000056171414024117000242370ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Mar-11 Floris Bruynooghe * Do not add stsizediff to general purpose * register (GPR) 30 as this is a non-volatile and * unused by the PowerOpen Environment, therefore * this was modifying a user register instead of the * frame pointer (which does not seem to exist). * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_ppc_linux.h000066400000000000000000000053311414024117000246060ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. * 31-Jul-12 Trevor Bowen * Changed memory constraints to register only. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" "add 30, 30, 11\n" : /* no outputs */ : "r" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_ppc_macosx.h000066400000000000000000000051221414024117000247370ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "; asm block 3\n" "\tmr r11, %0\n" "\tadd r1, r1, r11\n" "\tadd r30, r30, r11\n" : /* no outputs */ : "g" (stsizediff) : "r11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_ppc_unix.h000066400000000000000000000051561414024117000244370ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 07-Sep-05 (py-dev mailing list discussion) * removed 'r31' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 14-Jan-04 Bob Ippolito * added cr2-cr4 to the registers to be saved. * Open questions: Should we save FP registers? * What about vector registers? * Differences between darwin and unix? * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 04-Oct-02 Gustavo Niemeyer * Ported from MacOS version. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 29-Jun-02 Christian Tismer * Added register 13-29, 31 saves. The same way as * Armin Rigo did for the x86_unix version. * This seems to be now fully functional! * 04-Mar-02 Hye-Shik Chang * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 3 /* !!!!WARNING!!!! need to add "r31" in the next line if this header file * is meant to be compiled non-dynamically! */ #define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ "cr2", "cr3", "cr4" static int slp_switch(void) { register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ ("mr %0, 1" : "=g" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "mr 11, %0\n" "add 1, 1, 11\n" "add 30, 30, 11\n" : /* no outputs */ : "g" (stsizediff) : "11" ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("li %0, 0" : "=r" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_riscv_unix.h000066400000000000000000000013661414024117000250020ustar00rootroot00000000000000#define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \ "s6", "s7", "s8", "s9", "s10", "s11", "fs0", "fs1", \ "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", \ "fs10", "fs11" static int slp_switch(void) { register int ret; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mv %0, sp" : "=r" (stackref) : ); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "add sp, sp, %0\n\t" : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("mv %0, zero" : "=r" (ret) : ); return ret; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_s390_unix.h000066400000000000000000000053351414024117000243520ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 25-Jan-12 Alexey Borzenkov * Fixed Linux/S390 port to work correctly with * different optimization options both on 31-bit * and 64-bit. Thanks to Stefan Raabe for lots * of testing. * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 06-Oct-02 Gustavo Niemeyer * Ported to Linux/S390. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #ifdef __s390x__ #define STACK_MAGIC 20 /* 20 * 8 = 160 bytes of function call area */ #else #define STACK_MAGIC 24 /* 24 * 4 = 96 bytes of function call area */ #endif /* Technically, r11-r13 also need saving, but function prolog starts with stm(g) and since there are so many saved registers already it won't be optimized, resulting in all r6-r15 being saved */ #define REGS_TO_SAVE "r6", "r7", "r8", "r9", "r10", "r14", \ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" static int slp_switch(void) { register int ret; register long *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); #ifdef __s390x__ __asm__ volatile ("lgr %0, 15" : "=r" (stackref) : ); #else __asm__ volatile ("lr %0, 15" : "=r" (stackref) : ); #endif { SLP_SAVE_STATE(stackref, stsizediff); /* N.B. r11 may be used as the frame pointer, and in that case it cannot be clobbered and needs offsetting just like the stack pointer (but in cases where frame pointer isn't used we might clobber it accidentally). What's scary is that r11 is 2nd (and even 1st when GOT is used) callee saved register that gcc would chose for surviving function calls. However, since r6-r10 are clobbered above, their cost for reuse is reduced, so gcc IRA will chose them over r11 (not seeing r11 is implicitly saved), making it relatively safe to offset in all cases. :) */ __asm__ volatile ( #ifdef __s390x__ "agr 15, %0\n\t" "agr 11, %0" #else "ar 15, %0\n\t" "ar 11, %0" #endif : /* no outputs */ : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("lhi %0, 0" : "=r" (ret) : ); return ret; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_sparc_sun_gcc.h000066400000000000000000000053771414024117000254300ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 16-May-15 Alexey Borzenkov * Move stack spilling code inside save/restore functions * 30-Aug-13 Floris Bruynooghe Clean the register windows again before returning. This does not clobber the PIC register as it leaves the current window intact and is required for multi- threaded code to work correctly. * 08-Mar-11 Floris Bruynooghe * No need to set return value register explicitly * before the stack and framepointer are adjusted * as none of the other registers are influenced by * this. Also don't needlessly clean the windows * ('ta %0" :: "i" (ST_CLEAN_WINDOWS)') as that * clobbers the gcc PIC register (%l7). * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * added support for SunOS sparc with gcc */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #if defined(__sparcv9) #define SLP_FLUSHW __asm__ volatile ("flushw") #else #define SLP_FLUSHW __asm__ volatile ("ta 3") /* ST_FLUSH_WINDOWS */ #endif /* On sparc we need to spill register windows inside save/restore functions */ #define SLP_BEFORE_SAVE_STATE() SLP_FLUSHW #define SLP_BEFORE_RESTORE_STATE() SLP_FLUSHW static int slp_switch(void) { register int err; register int *stackref, stsizediff; /* Put current stack pointer into stackref. * Register spilling is done in save/restore. */ __asm__ volatile ("mov %%sp, %0" : "=r" (stackref)); { /* Thou shalt put SLP_SAVE_STATE into a local block */ /* Copy the current stack onto the heap */ SLP_SAVE_STATE(stackref, stsizediff); /* Increment stack and frame pointer by stsizediff */ __asm__ volatile ( "add %0, %%sp, %%sp\n\t" "add %0, %%fp, %%fp" : : "r" (stsizediff)); /* Copy new stack from it's save store on the heap */ SLP_RESTORE_STATE(); __asm__ volatile ("mov %1, %0" : "=r" (err) : "i" (0)); return err; } } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_x32_unix.h000066400000000000000000000027671414024117000242760ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 17-Aug-12 Fantix King * Ported from amd64. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 #define REGS_TO_SAVE "r12", "r13", "r14", "r15" static int slp_switch(void) { void* ebp; void* ebx; unsigned int csr; unsigned short cw; register int err; register int *stackref, stsizediff; __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("stmxcsr %0" : "=m" (csr)); __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); __asm__ ("movl %%esp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addl %0, %%esp\n" "addl %0, %%ebp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); } __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : REGS_TO_SAVE); __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/platform/switch_x64_masm.asm000066400000000000000000000034611414024117000245760ustar00rootroot00000000000000; ; stack switching code for MASM on x641 ; Kristjan Valur Jonsson, sept 2005 ; ;prototypes for our calls slp_save_state_asm PROTO slp_restore_state_asm PROTO pushxmm MACRO reg sub rsp, 16 .allocstack 16 movaps [rsp], reg ; faster than movups, but we must be aligned ; .savexmm128 reg, offset (don't know what offset is, no documentation) ENDM popxmm MACRO reg movaps reg, [rsp] ; faster than movups, but we must be aligned add rsp, 16 ENDM pushreg MACRO reg push reg .pushreg reg ENDM popreg MACRO reg pop reg ENDM .code slp_switch PROC FRAME ;realign stack to 16 bytes after return address push, makes the following faster sub rsp,8 .allocstack 8 pushxmm xmm15 pushxmm xmm14 pushxmm xmm13 pushxmm xmm12 pushxmm xmm11 pushxmm xmm10 pushxmm xmm9 pushxmm xmm8 pushxmm xmm7 pushxmm xmm6 pushreg r15 pushreg r14 pushreg r13 pushreg r12 pushreg rbp pushreg rbx pushreg rdi pushreg rsi sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) .allocstack 10h .endprolog lea rcx, [rsp+10h] ;load stack base that we are saving call slp_save_state_asm ;pass stackpointer, return offset in eax cmp rax, 1 je EXIT1 cmp rax, -1 je EXIT2 ;actual stack switch: add rsp, rax call slp_restore_state_asm xor rax, rax ;return 0 EXIT: add rsp, 10h popreg rsi popreg rdi popreg rbx popreg rbp popreg r12 popreg r13 popreg r14 popreg r15 popxmm xmm6 popxmm xmm7 popxmm xmm8 popxmm xmm9 popxmm xmm10 popxmm xmm11 popxmm xmm12 popxmm xmm13 popxmm xmm14 popxmm xmm15 add rsp, 8 ret EXIT1: mov rax, 1 jmp EXIT EXIT2: sar rax, 1 jmp EXIT slp_switch ENDP ENDpython-greenlet-1.1.2/src/greenlet/platform/switch_x64_masm.obj000066400000000000000000000020661414024117000245700ustar00rootroot00000000000000d†ŔëKć.textÜě P`.data@PŔ.pdata  @0@.xdata,*@@@.debug$SV@BHěHěD)<$HěD)4$HěD),$HěD)$$HěD)$HěD)$HěD) $HěD)$Hě)<$Hě)4$AWAVAUATUSWVHěHŤL$čHř„‚Hř˙„HŕčH3ŔHÄ^_[]A\A]A^A_(4$HÄ(<$HÄD($HÄD( $HÄD($HÄD($HÄD($$HÄD(,$HÄD(4$HÄD(<$HÄHÄĂHÇŔëŠHŃřë…r Ž   llh`gpf0ePdŔbĐ`ŕ^đXPG>5,#ń‚GD:\Dev\Compile\Greenlet\greenlet-hg\platform\switch_x64_masm.obj7<Đ xMicrosoft (R) Macro Assembler@comp.id x•˙˙.text.data.pdata .xdata,.debug$S  - 8Bslp_save_state_asmslp_restore_state_asmslp_switch$xdatasympython-greenlet-1.1.2/src/greenlet/platform/switch_x64_msvc.h000066400000000000000000000034151414024117000242570ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 26-Sep-02 Christian Tismer * again as a result of virtualized stack access, * the compiler used less registers. Needed to * explicit mention registers in order to get them saved. * Thanks to Jeff Senn for pointing this out and help. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 01-Mar-02 Christian Tismer * Initial final version after lots of iterations for i386. */ /* Avoid alloca redefined warning on mingw64 */ #ifndef alloca #define alloca _alloca #endif #define STACK_REFPLUS 1 #define STACK_MAGIC 0 /* Use the generic support for an external assembly language slp_switch function. */ #define EXTERNAL_ASM #ifdef SLP_EVAL /* This always uses the external masm assembly file. */ #endif /* * further self-processing support */ /* we have IsBadReadPtr available, so we can peek at objects */ /* #define STACKLESS_SPY #ifdef IMPLEMENT_STACKLESSMODULE #include "Windows.h" #define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) static int IS_ON_STACK(void*p) { int stackref; intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; } #endif */python-greenlet-1.1.2/src/greenlet/platform/switch_x86_msvc.h000066400000000000000000000046611414024117000242670ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 26-Sep-02 Christian Tismer * again as a result of virtualized stack access, * the compiler used less registers. Needed to * explicit mention registers in order to get them saved. * Thanks to Jeff Senn for pointing this out and help. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for sparc * 01-Mar-02 Christian Tismer * Initial final version after lots of iterations for i386. */ #define alloca _alloca #define STACK_REFPLUS 1 #ifdef SLP_EVAL #define STACK_MAGIC 0 /* Some magic to quell warnings and keep slp_switch() from crashing when built with VC90. Disable global optimizations, and the warning: frame pointer register 'ebp' modified by inline assembly code */ #pragma optimize("g", off) #pragma warning(disable:4731) static int slp_switch(void) { void* seh; register int *stackref, stsizediff; __asm mov eax, fs:[0] __asm mov [seh], eax __asm mov stackref, esp; /* modify EBX, ESI and EDI in order to get them preserved */ __asm mov ebx, ebx; __asm xchg esi, edi; { SLP_SAVE_STATE(stackref, stsizediff); __asm { mov eax, stsizediff add esp, eax add ebp, eax } SLP_RESTORE_STATE(); } __asm mov eax, [seh] __asm mov fs:[0], eax return 0; } /* re-enable ebp warning and global optimizations. */ #pragma optimize("g", on) #pragma warning(default:4731) #endif /* * further self-processing support */ /* we have IsBadReadPtr available, so we can peek at objects */ #define STACKLESS_SPY #ifdef IMPLEMENT_STACKLESSMODULE #include "Windows.h" #define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) static int IS_ON_STACK(void*p) { int stackref; int stackbase = ((int)&stackref) & 0xfffff000; return (int)p >= stackbase && (int)p < stackbase + 0x00100000; } #endif python-greenlet-1.1.2/src/greenlet/platform/switch_x86_unix.h000066400000000000000000000057741414024117000243100ustar00rootroot00000000000000/* * this is the internal transfer function. * * HISTORY * 3-May-13 Ralf Schmitt * Add support for strange GCC caller-save decisions * (ported from switch_aarch64_gcc.h) * 19-Aug-11 Alexey Borzenkov * Correctly save ebp, ebx and cw * 07-Sep-05 (py-dev mailing list discussion) * removed 'ebx' from the register-saved. !!!! WARNING !!!! * It means that this file can no longer be compiled statically! * It is now only suitable as part of a dynamic library! * 24-Nov-02 Christian Tismer * needed to add another magic constant to insure * that f in slp_eval_frame(PyFrameObject *f) * STACK_REFPLUS will probably be 1 in most cases. * gets included into the saved stack area. * 17-Sep-02 Christian Tismer * after virtualizing stack save/restore, the * stack size shrunk a bit. Needed to introduce * an adjustment STACK_MAGIC per platform. * 15-Sep-02 Gerd Woetzel * slightly changed framework for spark * 31-Avr-02 Armin Rigo * Added ebx, esi and edi register-saves. * 01-Mar-02 Samual M. Rushing * Ported from i386. */ #define STACK_REFPLUS 1 #ifdef SLP_EVAL /* #define STACK_MAGIC 3 */ /* the above works fine with gcc 2.96, but 2.95.3 wants this */ #define STACK_MAGIC 0 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) # define ATTR_NOCLONE __attribute__((noclone)) #else # define ATTR_NOCLONE #endif static int slp_switch(void) { int err; #ifdef _WIN32 void *seh; #endif void *ebp, *ebx; unsigned short cw; register int *stackref, stsizediff; __asm__ volatile ("" : : : "esi", "edi"); __asm__ volatile ("fstcw %0" : "=m" (cw)); __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); #ifdef _WIN32 __asm__ volatile ( "movl %%fs:0x0, %%eax\n" "movl %%eax, %0\n" : "=m" (seh) : : "eax"); #endif __asm__ ("movl %%esp, %0" : "=g" (stackref)); { SLP_SAVE_STATE(stackref, stsizediff); __asm__ volatile ( "addl %0, %%esp\n" "addl %0, %%ebp\n" : : "r" (stsizediff) ); SLP_RESTORE_STATE(); __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); } #ifdef _WIN32 __asm__ volatile ( "movl %0, %%eax\n" "movl %%eax, %%fs:0x0\n" : : "m" (seh) : "eax"); #endif __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); __asm__ volatile ("fldcw %0" : : "m" (cw)); __asm__ volatile ("" : : : "esi", "edi"); return err; } #endif /* * further self-processing support */ /* * if you want to add self-inspection tools, place them * here. See the x86_msvc for the necessary defines. * These features are highly experimental und not * essential yet. */ python-greenlet-1.1.2/src/greenlet/slp_platformselect.h000066400000000000000000000060271414024117000233050ustar00rootroot00000000000000/* * Platform Selection for Stackless Python */ #if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) && defined(_MSC_VER) #include "platform/switch_x86_msvc.h" /* MS Visual Studio on X86 */ #elif defined(MS_WIN64) && defined(_M_X64) && defined(_MSC_VER) || defined(__MINGW64__) #include "platform/switch_x64_msvc.h" /* MS Visual Studio on X64 */ #elif defined(__GNUC__) && defined(__amd64__) && defined(__ILP32__) #include "platform/switch_x32_unix.h" /* gcc on amd64 with x32 ABI */ #elif defined(__GNUC__) && defined(__amd64__) #include "platform/switch_amd64_unix.h" /* gcc on amd64 */ #elif defined(__GNUC__) && defined(__i386__) #include "platform/switch_x86_unix.h" /* gcc on X86 */ #elif defined(__GNUC__) && defined(__powerpc64__) && (defined(__linux__) || defined(__FreeBSD__)) #include "platform/switch_ppc64_linux.h" /* gcc on PowerPC 64-bit */ #elif defined(__GNUC__) && defined(__PPC__) && (defined(__linux__) || defined(__FreeBSD__)) #include "platform/switch_ppc_linux.h" /* gcc on PowerPC */ #elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) #include "platform/switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ #elif defined(__GNUC__) && defined(__powerpc64__) && defined(_AIX) #include "platform/switch_ppc64_aix.h" /* gcc on AIX/PowerPC 64-bit */ #elif defined(__GNUC__) && defined(_ARCH_PPC) && defined(_AIX) #include "platform/switch_ppc_aix.h" /* gcc on AIX/PowerPC */ #elif defined(__GNUC__) && defined(sparc) #include "platform/switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ #elif defined(__SUNPRO_C) && defined(sparc) && defined(sun) #include "platform/switch_sparc_sun_gcc.h" /* SunStudio on amd64 */ #elif defined(__SUNPRO_C) && defined(__amd64__) && defined(sun) #include "platform/switch_amd64_unix.h" /* SunStudio on amd64 */ #elif defined(__SUNPRO_C) && defined(__i386__) && defined(sun) #include "platform/switch_x86_unix.h" /* SunStudio on x86 */ #elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) #include "platform/switch_s390_unix.h" /* Linux/S390 */ #elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) #include "platform/switch_s390_unix.h" /* Linux/S390 zSeries (64-bit) */ #elif defined(__GNUC__) && defined(__arm__) #ifdef __APPLE__ #include #endif #if TARGET_OS_IPHONE #include "platform/switch_arm32_ios.h" /* iPhone OS on arm32 */ #else #include "platform/switch_arm32_gcc.h" /* gcc using arm32 */ #endif #elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) #include "platform/switch_mips_unix.h" /* Linux/MIPS */ #elif defined(__GNUC__) && defined(__aarch64__) #include "platform/switch_aarch64_gcc.h" /* Aarch64 ABI */ #elif defined(__GNUC__) && defined(__mc68000__) #include "platform/switch_m68k_gcc.h" /* gcc on m68k */ #elif defined(__GNUC__) && defined(__csky__) #include "platform/switch_csky_gcc.h" /* gcc on csky */ #elif defined(__GNUC__) && defined(__riscv) #include "platform/switch_riscv_unix.h" /* gcc on RISC-V */ #elif defined(__GNUC__) && defined(__alpha__) #include "platform/switch_alpha_unix.h" /* gcc on DEC Alpha */ #endif python-greenlet-1.1.2/src/greenlet/tests/000077500000000000000000000000001414024117000203675ustar00rootroot00000000000000python-greenlet-1.1.2/src/greenlet/tests/__init__.py000066400000000000000000000000001414024117000224660ustar00rootroot00000000000000python-greenlet-1.1.2/src/greenlet/tests/_test_extension.c000066400000000000000000000124321414024117000237470ustar00rootroot00000000000000/* 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); 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); 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"}, {NULL, NULL, 0, NULL}}; #if PY_MAJOR_VERSION >= 3 # 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) #else # define INITERROR return PyMODINIT_FUNC init_test_extension(void) #endif { PyObject* module = NULL; #if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); #else module = Py_InitModule(TEST_MODULE_NAME, test_methods); #endif if (module == NULL) { INITERROR; } PyGreenlet_Import(); #if PY_MAJOR_VERSION >= 3 return module; #endif } python-greenlet-1.1.2/src/greenlet/tests/_test_extension_cpp.cpp000066400000000000000000000062141414024117000251520ustar00rootroot00000000000000/* This is a set of functions used to test C++ exceptions are not * broken during greenlet switches */ #include "../greenlet.h" 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)(int depth); static PyObject* (*p_test_exception_switch_recurse)(int depth, int left); static void test_exception_throw(int depth) { throw exception_t(depth); } 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 { PyGreenlet_Switch(self->parent, NULL, NULL); p_test_exception_throw(depth); PyErr_SetString(PyExc_RuntimeError, "throwing C++ exception didn't work"); } catch (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* self, PyObject* args) { int depth; if (!PyArg_ParseTuple(args, "i", &depth)) return NULL; return p_test_exception_switch_recurse(depth, depth); } static PyMethodDef test_methods[] = { {"test_exception_switch", (PyCFunction)&test_exception_switch, METH_VARARGS, "Switches to parent twice, to test exception handling and greenlet " "switching."}, {NULL, NULL, 0, NULL}}; #if PY_MAJOR_VERSION >= 3 # define INITERROR return 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) #else # define INITERROR return PyMODINIT_FUNC init_test_extension_cpp(void) #endif { PyObject* module = NULL; #if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); #else module = Py_InitModule("greenlet.tests._test_extension_cpp", test_methods); #endif if (module == NULL) { INITERROR; } PyGreenlet_Import(); if (_PyGreenlet_API == NULL) { INITERROR; } p_test_exception_throw = test_exception_throw; p_test_exception_switch_recurse = test_exception_switch_recurse; #if PY_MAJOR_VERSION >= 3 return module; #endif } python-greenlet-1.1.2/src/greenlet/tests/test_contextvars.py000066400000000000000000000217651414024117000243730ustar00rootroot00000000000000import unittest import gc import sys from functools import partial from greenlet import greenlet from greenlet import getcurrent try: from contextvars import Context from contextvars import ContextVar from contextvars import copy_context except ImportError: Context = ContextVar = copy_context = None # We don't support testing if greenlet's built-in context var support is disabled. @unittest.skipUnless(Context is not None, "ContextVar not supported") class ContextVarsTests(unittest.TestCase): def _new_ctx_run(self, *args, **kwargs): return copy_context().run(*args, **kwargs) def _increment(self, greenlet_id, ctx_var, callback, counts, expect): 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): id_var = ContextVar("id", default=None) 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, ctx_var=id_var, 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. self.assertEqual(let.gr_context is None, propagate_by == "run") 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): id_var = ContextVar("id", default=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 attr"): 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 ctx = Context() var = ContextVar("var", default=None) is_running = threading.Event() should_suspend = threading.Event() did_suspend = threading.Event() should_exit = threading.Event() holder = [] def greenlet_in_thread_fn(): var.set(1) is_running.set() should_suspend.wait() var.set(2) getcurrent().parent.switch() holder.append(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() gr.switch() thread = threading.Thread(target=thread_fn, daemon=True) thread.start() is_running.wait() 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() # OK to access and modify context if greenlet is suspended self.assertIs(gr.gr_context, ctx) self.assertEqual(gr.gr_context[var], 2) gr.gr_context = None should_exit.set() thread.join() 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) @unittest.skipIf(Context is not None, "ContextVar supported") class NoContextVarsTests(unittest.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 python-greenlet-1.1.2/src/greenlet/tests/test_cpp.py000066400000000000000000000007501414024117000225640ustar00rootroot00000000000000from __future__ import print_function from __future__ import absolute_import import unittest import greenlet from . import _test_extension_cpp class CPPTests(unittest.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) python-greenlet-1.1.2/src/greenlet/tests/test_extension_interface.py000066400000000000000000000047311414024117000260410ustar00rootroot00000000000000from __future__ import print_function from __future__ import absolute_import import sys import unittest import greenlet from . import _test_extension class CAPITests(unittest.TestCase): def test_switch(self): self.assertEqual( 50, _test_extension.test_switch(greenlet.greenlet(lambda: 50))) def test_switch_kwargs(self): def foo(x, y): return x * y g = greenlet.greenlet(foo) self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2)) def test_setparent(self): 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(): 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") if __name__ == '__main__': unittest.main() python-greenlet-1.1.2/src/greenlet/tests/test_gc.py000066400000000000000000000055141414024117000223760ustar00rootroot00000000000000import gc import sys import unittest import weakref import greenlet class GCTests(unittest.TestCase): def test_dead_circular_ref(self): o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch()) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage) if greenlet.GREENLET_USE_GC: # These only work with greenlet gc support def test_circular_greenlet(self): class circular_greenlet(greenlet.greenlet): pass o = circular_greenlet() o.self = o o = weakref.ref(o) gc.collect() self.assertTrue(o() is None) 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.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage) 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() 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() python-greenlet-1.1.2/src/greenlet/tests/test_generator.py000066400000000000000000000024001414024117000237620ustar00rootroot00000000000000import unittest from greenlet import greenlet class genlet(greenlet): 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 else: raise StopIteration # Hack: Python < 2.6 compatibility 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(unittest.TestCase): def test_generator(self): seen = [] def g(n): for i in range(n): seen.append(i) Yield(i) g = generator(g) for k 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]) python-greenlet-1.1.2/src/greenlet/tests/test_generator_nested.py000066400000000000000000000067731414024117000253450ustar00rootroot00000000000000import unittest from greenlet import greenlet class genlet(greenlet): def __init__(self, *args, **kwds): self.args = args self.kwds = kwds self.child = None def run(self): 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 else: raise StopIteration # Hack: Python < 2.6 compatibility 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 Genlet(genlet): fn = (func,) return Genlet # ____________________________________________________________ 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 [Yield([e] + p) for p in perms([x for x in l if x != e])] 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(unittest.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]) def test_permutations(self): gen_perms = perms(list(range(4))) permutations = list(gen_perms) self.assertEqual(len(permutations), 4 * 3 * 2 * 1) self.assertTrue([0, 1, 2, 3] in permutations) self.assertTrue([3, 2, 1, 0] in 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 k 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) python-greenlet-1.1.2/src/greenlet/tests/test_greenlet.py000066400000000000000000000556031414024117000236160ustar00rootroot00000000000000import gc import sys import time import threading import unittest from abc import ABCMeta, abstractmethod from greenlet import greenlet # We manually manage locks in many tests # pylint:disable=consider-using-with 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 explicitely check the propagation rules. def crasher(exc): raise exc g1 = greenlet(crasher, parent=g) g1.switch(exc) class TestGreenlet(unittest.TestCase): def test_simple(self): lst = [] def f(): lst.append(1) greenlet.getcurrent().parent.switch() lst.append(3) g = greenlet(f) lst.append(0) g.switch() lst.append(2) g.switch() lst.append(4) self.assertEqual(lst, list(range(5))) def test_parent_equals_None(self): g = greenlet(parent=None) self.assertIsNotNone(g) self.assertIs(g.parent, greenlet.getcurrent()) def test_run_equals_None(self): g = greenlet(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 = greenlet(f) h = greenlet(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(1) greenlet.getcurrent().parent.switch() def g(): lst.append(1) g = greenlet(f) g.switch() lst.append(1) g = greenlet(g) g.switch() self.assertEqual(len(lst), 3) self.assertEqual(sys.getrefcount(g), 2) def test_threads(self): success = [] def f(): self.test_simple() 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() self.assertEqual(len(success), len(ths)) def test_exception(self): seen = [] g1 = greenlet(fmain) g2 = greenlet(fmain) g1.switch(seen) g2.switch(seen) g2.parent = g1 self.assertEqual(seen, []) self.assertRaises(SomeError, g2.switch) self.assertEqual(seen, [SomeError]) g2.switch() self.assertEqual(seen, [SomeError]) def test_send_exception(self): seen = [] g1 = greenlet(fmain) g1.switch(seen) self.assertRaises(KeyError, send_exception, g1, KeyError) self.assertEqual(seen, [KeyError]) def test_dealloc(self): seen = [] g1 = greenlet(fmain) g2 = greenlet(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_other_thread(self): seen = [] someref = [] lock = threading.Lock() lock.acquire() lock2 = threading.Lock() lock2.acquire() def f(): g1 = greenlet(fmain) g1.switch(seen) someref.append(g1) del g1 gc.collect() lock.release() lock2.acquire() greenlet() # trigger release lock.release() lock2.acquire() t = threading.Thread(target=f) t.start() lock.acquire() 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, []) lock2.release() lock.acquire() self.assertEqual(seen, [greenlet.GreenletExit]) lock2.release() t.join() 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 = greenlet(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 = greenlet(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() t2.join() def test_switch_kwargs(self): def run(a, b): self.assertEqual(a, 4) self.assertEqual(b, 2) return 42 x = greenlet(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 = greenlet(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 = {} error = None created_event = threading.Event() done_event = threading.Event() def run(): data['g'] = greenlet(lambda: None) created_event.set() done_event.wait() thread = threading.Thread(target=run) thread.start() created_event.wait() try: data['g'].switch() except greenlet.error: error = sys.exc_info()[1] self.assertIsNotNone(error, "greenlet.error was not raised!") done_event.set() thread.join() def test_exc_state(self): def f(): try: raise ValueError('fun') except: # pylint:disable=bare-except exc_info = sys.exc_info() greenlet(h).switch() self.assertEqual(exc_info, sys.exc_info()) def h(): self.assertEqual(sys.exc_info(), (None, None, None)) greenlet(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 = greenlet(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_threaded_reparent(self): data = {} created_event = threading.Event() done_event = threading.Event() def run(): data['g'] = greenlet(lambda: None) created_event.set() done_event.wait() def blank(): greenlet.getcurrent().parent.switch() def setparent(g, value): g.parent = value thread = threading.Thread(target=run) thread.start() created_event.wait() g = greenlet(blank) g.switch() self.assertRaises(ValueError, setparent, g, data['g']) done_event.set() thread.join() def test_deepcopy(self): import copy self.assertRaises(TypeError, copy.copy, greenlet()) self.assertRaises(TypeError, copy.deepcopy, greenlet()) def test_parent_restored_on_kill(self): hub = greenlet(lambda: None) main = greenlet.getcurrent() result = [] def worker(): try: # Wait to be killed main.switch() except greenlet.GreenletExit: # Resurrect and switch to parent result.append(greenlet.getcurrent().parent) result.append(greenlet.getcurrent()) hub.switch() g = greenlet(worker, parent=hub) g.switch() del g self.assertTrue(result) self.assertEqual(result[0], main) self.assertEqual(result[1].parent, hub) def test_parent_return_failure(self): # No run causes AttributeError on switch g1 = greenlet() # Greenlet that implicitly switches to parent g2 = greenlet(lambda: None, parent=g1) # AttributeError should propagate to us, no fatal errors self.assertRaises(AttributeError, g2.switch) def test_throw_exception_not_lost(self): class mygreenlet(greenlet): def __getattribute__(self, name): try: raise Exception() except: # pylint:disable=bare-except pass return greenlet.__getattribute__(self, name) g = mygreenlet(lambda: None) self.assertRaises(SomeError, g.throw, SomeError()) def test_throw_doesnt_crash(self): result = [] def worker(): greenlet.getcurrent().parent.switch() def creator(): g = greenlet(worker) g.switch() result.append(g) t = threading.Thread(target=creator) t.start() t.join() self.assertRaises(greenlet.error, result[0].throw, SomeError()) def test_recursive_startup(self): class convoluted(greenlet): def __init__(self): greenlet.__init__(self) self.count = 0 def __getattribute__(self, name): if name == 'run' and self.count == 0: self.count = 1 self.switch(43) return greenlet.__getattribute__(self, name) def run(self, value): while True: self.parent.switch(value) g = convoluted() self.assertEqual(g.switch(42), 43) def test_unexpected_reparenting(self): another = [] def worker(): g = greenlet(lambda: None) another.append(g) g.switch() t = threading.Thread(target=worker) t.start() t.join() class convoluted(greenlet): def __getattribute__(self, name): if name == 'run': self.parent = another[0] # pylint:disable=attribute-defined-outside-init return greenlet.__getattribute__(self, name) g = convoluted(lambda: None) self.assertRaises(greenlet.error, g.switch) 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 = [greenlet(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() 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 immediately try: greenlet.getcurrent().parent.switch(value) finally: seen.append(greenlet.getcurrent()) def initiator(): return 42 # implicitly falls thru to parent worker = [greenlet(worker)] worker[0].switch() # prime worker initiator = greenlet(initiator, worker[0]) value = initiator.switch() self.assertTrue(seen) self.assertEqual(value, 42) def test_tuple_subclass(self): if sys.version_info[0] > 2: # There's no apply in Python 3.x def _apply(func, a, k): func(*a, **k) else: _apply = apply # pylint:disable=undefined-variable 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 = greenlet(switchapply) self.assertEqual(g.switch(), kwargs) def test_abstract_subclasses(self): AbstractSubclass = ABCMeta( 'AbstractSubclass', (greenlet,), {'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() return False last = greenlet() 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() 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(greenlet): 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(greenlet): 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() # 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() 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). time.sleep(1) self.assertEqual(sys.getrefcount(MyGreenlet), initial_refs) class TestRepr(unittest.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 = greenlet(run) r = g.switch() self.assertEndsWith(r, ' suspended active started main>') def test_initial(self): r = repr(greenlet()) 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>') r = repr(t.main_glet) # main greenlets, even from dead threads, never really appear dead # TODO: Can we find a better way to differentiate that? assert not t.main_glet.dead self.assertEndsWith(r, ' suspended active started main>') def test_dead(self): g = greenlet(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 = greenlet(lambda: None) g_not_started = greenlet(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, ) if __name__ == '__main__': unittest.main() python-greenlet-1.1.2/src/greenlet/tests/test_leaks.py000066400000000000000000000150251414024117000231020ustar00rootroot00000000000000import unittest import sys import gc import time import weakref import threading import greenlet class TestLeaks(unittest.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) assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0 def recycle_threads(self): # 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() 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() 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() 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_issue251_killing_cross_thread_leaks_list(self, manually_collect_background=True): # 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). # For the test to be valid, even empty lists have to be tracked by the # GC assert gc.is_tracked([]) def count_objects(kind=list): # pylint:disable=unidiomatic-typecheck # Collect the garbage. for _ in range(3): gc.collect() gc.collect() return sum( 1 for x in gc.get_objects() if type(x) is kind ) # XXX: The main greenlet of a dead thread is only released # when one of the proper greenlet APIs is used from a different # running thread. See #252 (https://github.com/python-greenlet/greenlet/issues/252) greenlet.getcurrent() greenlets_before = count_objects(greenlet.greenlet) background_glet_running = threading.Event() background_glet_killed = threading.Event() background_greenlets = [] def background_greenlet(): # Throw control back to the main greenlet. greenlet.getcurrent().parent.switch() def background_thread(): glet = greenlet.greenlet(background_greenlet) 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() # 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() lists_before = count_objects() 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 # Free the background main greenlet by forcing greenlet to notice a difference. greenlet.getcurrent() greenlets_after = count_objects(greenlet.greenlet) lists_after = count_objects() # 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) self.assertEqual(greenlets_before, greenlets_after) @unittest.expectedFailure def test_issue251_issue252_need_to_collect_in_background(self): # This still fails 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. self.test_issue251_killing_cross_thread_leaks_list(manually_collect_background=False) python-greenlet-1.1.2/src/greenlet/tests/test_stack_saved.py000066400000000000000000000007021414024117000242660ustar00rootroot00000000000000import greenlet import unittest class Test(unittest.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() assert x > 0, x assert g._stack_saved > 0, g._stack_saved g.switch() assert g._stack_saved == 0, g._stack_saved python-greenlet-1.1.2/src/greenlet/tests/test_throw.py000066400000000000000000000053031414024117000231440ustar00rootroot00000000000000import sys import unittest from greenlet import greenlet def switch(*args): return greenlet.getcurrent().parent.switch(*args) class ThrowTests(unittest.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" else: return "normal exit" def f2(): main.switch("from f2") g1 = greenlet(f1) g2 = greenlet(f2, parent=g1) 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) python-greenlet-1.1.2/src/greenlet/tests/test_tracing.py000066400000000000000000000164371414024117000234420ustar00rootroot00000000000000import sys import unittest import greenlet 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(unittest.TestCase): """ Tests of ``greenlet.settrace()`` """ def test_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_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)), ]) 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(unittest.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) 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__'), ]) 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__'), ]) python-greenlet-1.1.2/src/greenlet/tests/test_version.py000077500000000000000000000023151414024117000234710ustar00rootroot00000000000000#! /usr/bin/env python from __future__ import absolute_import from __future__ import print_function import sys import os import unittest import greenlet class VersionTests(unittest.TestCase): 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: raise unittest.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__) python-greenlet-1.1.2/src/greenlet/tests/test_weakref.py000066400000000000000000000015641414024117000234320ustar00rootroot00000000000000import gc import greenlet import weakref import unittest class WeakRefTests(unittest.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]) python-greenlet-1.1.2/tox.ini000066400000000000000000000013141414024117000161430ustar00rootroot00000000000000[tox] envlist = py27,py35,py36,py37,py38,py39,py310,docs [testenv] commands = python -m unittest discover -v greenlet.tests sphinx-build -b doctest -d docs/_build/doctrees-{envname} docs docs/_build/doctest-{envname} sitepackages = False extras = test docs [testenv:py310] # See tests.yml commands = python -m unittest discover -v greenlet.tests [testenv:docs] # usedevelop to save rebuilding the extension usedevelop = true # We can't use Python 3.10 yet, so specify fully what we need. basepython = python3.9 commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest extras = docs