diff -Nru cython-0.20.1+1~201611251650-6686/2to3-fixers.txt cython-0.20.1+1~202203241016-9537/2to3-fixers.txt
--- cython-0.20.1+1~201611251650-6686/2to3-fixers.txt 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/2to3-fixers.txt 1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-lib2to3.fixes.fix_unicode
diff -Nru cython-0.20.1+1~201611251650-6686/appveyor/install.ps1 cython-0.20.1+1~202203241016-9537/appveyor/install.ps1
--- cython-0.20.1+1~201611251650-6686/appveyor/install.ps1 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/appveyor/install.ps1 2022-03-24 10:16:46.000000000 +0000
@@ -1,6 +1,6 @@
# Sample script to install Python and pip under Windows
# Authors: Olivier Grisel and Kyle Kastner
-# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
+# License: CC0 1.0 Universal: https://creativecommons.org/publicdomain/zero/1.0/
$PYTHON_BASE_URL = "https://www.python.org/ftp/python/"
$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
@@ -48,12 +48,14 @@
$installer_exe = ($py_major + $py_minor) -as [int] -ge 35
if ($installer_exe) {
$arch_suffix = @{"32"="";"64"="-amd64"}[$architecture]
- $filename = "python-" + $python_version + $arch_suffix + ".exe"
+ $dl_filename = "python-" + $python_version + $arch_suffix + ".exe"
+ $filename = "python-" + $py_major + "." + $py_minor + $arch_suffix + ".exe"
} else {
$arch_suffix = @{"32"="";"64"=".amd64"}[$architecture]
- $filename = "python-" + $python_version + $arch_suffix + ".msi"
+ $dl_filename = "python-" + $python_version + $arch_suffix + ".msi"
+ $filename = "python-" + $py_major + "." + $py_minor + $arch_suffix + ".msi"
}
- $url = $PYTHON_BASE_URL + $python_version + "/" + $filename
+ $url = $PYTHON_BASE_URL + $python_version + "/" + $dl_filename
$filepath = Download $url $filename $DOWNLOADS
Write-Host "Installing" $filename "to" $python_home
if ($installer_exe) {
@@ -99,7 +101,8 @@
}
function main () {
- InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
+ $full_version = $env:PYTHON_VERSION + ".0"
+ InstallPython $full_version $env:PYTHON_ARCH $env:PYTHON
InstallPip $env:PYTHON
InstallPipPackage $env:PYTHON setuptools
InstallPipPackage $env:PYTHON wheel
diff -Nru cython-0.20.1+1~201611251650-6686/appveyor.yml cython-0.20.1+1~202203241016-9537/appveyor.yml
--- cython-0.20.1+1~201611251650-6686/appveyor.yml 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/appveyor.yml 2022-03-24 10:16:46.000000000 +0000
@@ -4,57 +4,107 @@
global:
# 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 intepreter
- # See: http://stackoverflow.com/a/13751649/163740
+ # /E:ON and /V:ON options are not enabled in the batch script interpreter
+ # See: https://stackoverflow.com/questions/11267463/compiling-python-modules-on-windows-x64/13751649#13751649
WITH_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
+ BACKEND: c
+ PARALLEL: "-j4"
+ EXTRA_CFLAGS: ""
matrix:
- - PYTHON: "C:\\Python26"
- PYTHON_VERSION: "2.6.6"
- PYTHON_ARCH: "32"
-
- PYTHON: "C:\\Python27"
- PYTHON_VERSION: "2.7.9"
+ PYTHON_VERSION: "2.7"
PYTHON_ARCH: "32"
+ PYTHONIOENCODING: "utf-8"
+ PARALLEL: ""
+
+ - PYTHON: "C:\\Python27-x64"
+ PYTHON_VERSION: "2.7"
+ PYTHON_ARCH: "64"
+ PYTHONIOENCODING: "utf-8"
+ PARALLEL: ""
- - PYTHON: "C:\\Python33"
- PYTHON_VERSION: "3.3.5"
+ - PYTHON: "C:\\Python39"
+ PYTHON_VERSION: "3.9"
PYTHON_ARCH: "32"
- - PYTHON: "C:\\Python34"
- PYTHON_VERSION: "3.4.3"
+ - PYTHON: "C:\\Python39-x64"
+ PYTHON_VERSION: "3.9"
+ PYTHON_ARCH: "64"
+
+ - PYTHON: "C:\\Python38"
+ PYTHON_VERSION: "3.8"
PYTHON_ARCH: "32"
- - PYTHON: "C:\\Python35"
- PYTHON_VERSION: "3.5.0"
+ - PYTHON: "C:\\Python38-x64"
+ PYTHON_VERSION: "3.8"
+ PYTHON_ARCH: "64"
+ EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1"
+
+ - PYTHON: "C:\\Python38-x64"
+ PYTHON_VERSION: "3.8"
+ PYTHON_ARCH: "64"
+ BACKEND: c,cpp
+
+ - PYTHON: "C:\\Python37"
+ PYTHON_VERSION: "3.7"
PYTHON_ARCH: "32"
+ BACKEND: c,cpp
- - PYTHON: "C:\\Python26-x64"
- PYTHON_VERSION: "2.6.6"
+ - PYTHON: "C:\\Python37-x64"
+ PYTHON_VERSION: "3.7"
PYTHON_ARCH: "64"
- - PYTHON: "C:\\Python27-x64"
- PYTHON_VERSION: "2.7.9"
+ - PYTHON: "C:\\Python37-x64"
+ PYTHON_VERSION: "3.7"
PYTHON_ARCH: "64"
+ EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1"
- - PYTHON: "C:\\Python33-x64"
- PYTHON_VERSION: "3.3.5"
+ - PYTHON: "C:\\Python37-x64"
+ PYTHON_VERSION: "3.7"
PYTHON_ARCH: "64"
+ BACKEND: cpp
- - PYTHON: "C:\\Python34-x64"
- PYTHON_VERSION: "3.4.3"
+ - PYTHON: "C:\\Python36"
+ PYTHON_VERSION: "3.6"
+ PYTHON_ARCH: "32"
+
+ - PYTHON: "C:\\Python36-x64"
+ PYTHON_VERSION: "3.6"
PYTHON_ARCH: "64"
+ - PYTHON: "C:\\Python35"
+ PYTHON_VERSION: "3.5"
+ PYTHON_ARCH: "32"
+
- PYTHON: "C:\\Python35-x64"
- PYTHON_VERSION: "3.5.0"
+ PYTHON_VERSION: "3.5"
+ PYTHON_ARCH: "64"
+
+ - PYTHON: "C:\\Python34"
+ PYTHON_VERSION: "3.4"
+ PYTHON_ARCH: "32"
+ PARALLEL: ""
+
+ - PYTHON: "C:\\Python34-x64"
+ PYTHON_VERSION: "3.4"
+ PYTHON_ARCH: "64"
+ PARALLEL: ""
+
+ - PYTHON: "C:\\Python27-x64"
+ PYTHON_VERSION: "2.7"
PYTHON_ARCH: "64"
+ BACKEND: cpp
+ PYTHONIOENCODING: "utf-8"
+ PARALLEL: ""
clone_depth: 5
branches:
only:
- master
- - 0.23.x
+ - release
+ - 0.29.x
init:
- "ECHO Python %PYTHON_VERSION% (%PYTHON_ARCH%bit) from %PYTHON%"
@@ -67,11 +117,16 @@
build: off
build_script:
+ - "%WITH_ENV% %PYTHON%\\python.exe setup.py build_ext %PARALLEL%"
+ - "%WITH_ENV% %PYTHON%\\python.exe setup.py build_ext --inplace"
- "%WITH_ENV% %PYTHON%\\python.exe setup.py bdist_wheel"
test: off
test_script:
- - "%WITH_ENV% %PYTHON%\\python.exe runtests.py -vv --no-cpp"
+ - "%PYTHON%\\Scripts\\pip.exe install -r test-requirements.txt"
+ - "%PYTHON%\\Scripts\\pip.exe install win_unicode_console"
+ - "set CFLAGS=/Od /W3 %EXTRA_CFLAGS%"
+ - "%WITH_ENV% %PYTHON%\\python.exe runtests.py -vv --backend=%BACKEND% --no-code-style -j5"
artifacts:
- path: dist\*
diff -Nru cython-0.20.1+1~201611251650-6686/bin/cython_freeze cython-0.20.1+1~202203241016-9537/bin/cython_freeze
--- cython-0.20.1+1~201611251650-6686/bin/cython_freeze 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/bin/cython_freeze 2022-03-24 10:16:46.000000000 +0000
@@ -3,7 +3,7 @@
Create a C file for embedding one or more Cython source files.
Requires Cython 0.11.2 (or perhaps newer).
-See Demos/freeze/README.txt for more details.
+See Demos/freeze/README.rst for more details.
"""
from __future__ import print_function
diff -Nru cython-0.20.1+1~201611251650-6686/bin/cython-generate-lexicon.py cython-0.20.1+1~202203241016-9537/bin/cython-generate-lexicon.py
--- cython-0.20.1+1~201611251650-6686/bin/cython-generate-lexicon.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/bin/cython-generate-lexicon.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+
+#
+# Updates Cython's Lexicon.py with the unicode characters that are accepted as
+# identifiers. Should be run with the most recent version of Python possible
+# to ensure that Lexicon is as complete as possible.
+#
+# Python3 only (it relies on str.isidentifier which is a Python 3 addition)
+#
+# Run with either
+# --overwrite to update the existing Lexicon.py file
+# --here to create a copy of Lexicon.py in the current directory
+
+import functools
+import re
+import os
+import sys
+
+# Make sure we import the right Cython
+cythonpath, _ = os.path.split(os.path.realpath(__file__)) # bin directory
+cythonpath, _ = os.path.split(cythonpath)
+if os.path.exists(os.path.join(cythonpath, "Cython")):
+ sys.path.insert(0, cythonpath)
+ print("Found (and using) local cython directory")
+# else we aren't in a development directory
+
+from Cython.Compiler import Lexicon
+
+
+def main():
+ arg = '--overwrite'
+ if len(sys.argv) == 2:
+ arg = sys.argv[1]
+ if len(sys.argv) > 2 or arg not in ['--overwrite','--here']:
+ print("""Call the script with either:
+ --overwrite to update the existing Lexicon.py file (default)
+ --here to create an version of Lexicon.py in the current directory
+""")
+ return
+
+ generated_code = (
+ f"# generated with:\n"
+ f"# {sys.implementation.name} {sys.version.splitlines()[0].strip()}\n"
+ "\n"
+ f"{generate_character_sets()}\n"
+ )
+
+ print("Reading file", Lexicon.__file__)
+ with open(Lexicon.__file__, 'r') as f:
+ parts = re.split(r"(# (?:BEGIN|END) GENERATED CODE\n?)", f.read())
+
+ if len(parts) not in (4,5) or ' GENERATED CODE' not in parts[1] or ' GENERATED CODE' not in parts[3]:
+ print("Warning: generated code section not found - code not inserted")
+ return
+
+ parts[2] = generated_code
+ output = "".join(parts)
+
+ if arg == "--here":
+ outfile = "Lexicon.py"
+ else:
+ assert arg == "--overwrite"
+ outfile = Lexicon.__file__
+
+ print("Writing to file", outfile)
+ with open(outfile, 'w') as f:
+ f.write(output)
+
+
+# The easiest way to generate an appropriate character set is just to use the str.isidentifier method
+# An alternative approach for getting character sets is at https://stackoverflow.com/a/49332214/4657412
+@functools.lru_cache()
+def get_start_characters_as_number():
+ return [ i for i in range(sys.maxunicode) if str.isidentifier(chr(i)) ]
+
+
+def get_continue_characters_as_number():
+ return [ i for i in range(sys.maxunicode) if str.isidentifier('a'+chr(i)) ]
+
+
+def get_continue_not_start_as_number():
+ start = get_start_characters_as_number()
+ cont = get_continue_characters_as_number()
+ assert set(start) <= set(cont), \
+ "We assume that all identifier start characters are also continuation characters."
+ return sorted(set(cont).difference(start))
+
+
+def to_ranges(char_num_list):
+ # Convert the large lists of character digits to
+ # list of characters
+ # a list pairs of characters representing closed ranges
+ char_num_list = sorted(char_num_list)
+ first_good_val = char_num_list[0]
+
+ single_chars = []
+ ranges = []
+ for n in range(1, len(char_num_list)):
+ if char_num_list[n]-1 != char_num_list[n-1]:
+ # discontinuous
+ if first_good_val == char_num_list[n-1]:
+ single_chars.append(chr(char_num_list[n-1]))
+ else:
+ ranges.append(chr(first_good_val) + chr(char_num_list[n-1]))
+ first_good_val = char_num_list[n]
+
+ return ''.join(single_chars), ''.join(ranges)
+
+
+def make_split_strings(chars, splitby=60, indent=" "):
+ lines = [f'u"{chars[i:i+splitby]}"' for i in range(0, len(chars), splitby)]
+ return indent + f"\n{indent}".join(lines)
+
+
+def generate_character_sets():
+ declarations = []
+ for char_type, char_generator in [
+ ("unicode_start_ch", get_start_characters_as_number),
+ ("unicode_continuation_ch", get_continue_not_start_as_number),
+ ]:
+ for set_type, chars in zip(("any", "range"), to_ranges(char_generator())):
+ declarations.append(
+ f"{char_type}_{set_type} = (\n"
+ f"{make_split_strings(chars)}\n"
+ f")\n"
+ )
+
+ return "".join(declarations)
+
+
+if __name__ == "__main__":
+ main()
diff -Nru cython-0.20.1+1~201611251650-6686/bin/cythonrun cython-0.20.1+1~202203241016-9537/bin/cythonrun
--- cython-0.20.1+1~201611251650-6686/bin/cythonrun 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/bin/cythonrun 2022-03-24 10:16:46.000000000 +0000
@@ -9,7 +9,7 @@
python cythonrun somefile.py [ARGS]
"""
-from Cython.Build.BuildExecutable import build, build_and_run
+from Cython.Build.BuildExecutable import build_and_run
if __name__ == '__main__':
import sys
diff -Nru cython-0.20.1+1~201611251650-6686/CHANGES.rst cython-0.20.1+1~202203241016-9537/CHANGES.rst
--- cython-0.20.1+1~201611251650-6686/CHANGES.rst 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/CHANGES.rst 2022-03-24 10:16:46.000000000 +0000
@@ -2,7 +2,2625 @@
Cython Changelog
================
-0.25 (2016-??-??)
+3.0.0 alpha 11 (2022-0?-??)
+===========================
+
+Features added
+--------------
+
+* A new decorator ``@cython.dataclasses.dataclass`` was implemented that provides
+ compile time dataclass generation capabilities to ``cdef`` classes (extension types).
+ Patch by David Woods. (Github issue :issue:`2903`)
+
+* Named expressions (PEP 572) aka. assignment expressions (aka. the walrus operator
+ ``:=``) were implemented.
+ Patch by David Woods. (Github issue :issue:`2636`)
+
+* The ``cythonize`` command has a new option ``-M`` to generate ``.dep`` dependency
+ files for the compilation unit. This can be used by external build tools to track
+ these dependencies. Already available in Cython :ref:`0.29.27`.
+ Patch by Evgeni Burovski. (Github issue :issue:`1214`)
+
+* ``cythonize()`` and the corresponding CLI command now regenerate the output files
+ also when they already exist but were generated by a different Cython version.
+
+* The environment variable ``CYTHON_FORCE_REGEN=1`` can be used to force ``cythonize``
+ to regenerate the output files regardless of modification times and changes.
+
+* The generated C code now compiles in CPython 3.11a4.
+ (Github issue :issue:`4500`)
+
+Bugs fixed
+----------
+
+* Includes all bug-fixes from the :ref:`0.29.27` release.
+
+Other changes
+-------------
+
+* When using type annotations, ``func(x: list)`` or ``func(x: ExtType)`` (and other
+ Python builtin or extension types) now disallow ``None`` as input argument to ``x``.
+ This is consistent with the normal typing semantics in Python, and was a common gotcha
+ for users who did not expect ``None`` to be allowed as input. To allow ``None``, use
+ ``typing.Optional`` as in ``func(x: Optional[list])``. ``None`` is also automatically
+ allowed when it is used as default argument, i.e. ``func(x: list = None)``.
+ Note that, for backwards compatibility reasons, this does not apply when using Cython's
+ C notation, as in ``func(list x)``. Here, ``None`` is still allowed, as always.
+ (Github issues :issue:`3883`, :issue:`2696`)
+
+
+3.0.0 alpha 10 (2022-01-06)
+===========================
+
+Features added
+--------------
+
+* ``Cython.Distutils.build_ext`` now uses ``cythonize()`` internally (previously
+ known as ``new_build_ext``), while still supporting the options that were
+ available in the old implementation (``old_build_ext``).
+ Patch by Matus Valo. (Github issue :issue:`3541`)
+
+* ``pyximport`` now uses ``cythonize()`` internally.
+ Patch by Matus Valo. (Github issue :issue:`2304`)
+
+* ``__del__(self)`` on extension types now maps to ``tp_finalize`` in Python 3.
+ Original patch by ax487. (Github issue :issue:`3612`)
+
+* Conversion from Python dict to C++ map now supports arbitrary Python mappings,
+ not just dicts.
+
+* Direct assignments to C++ references are now allowed.
+ Patch by David Woods. (Github issue :issue:`1863`)
+
+* An initial set of adaptations for GraalVM Python was implemented. Note that
+ this does not imply any general support for this target or that your code
+ will work at all in this environment. But testing should be possible now.
+ Patch by David Woods. (Github issue :issue:`4328`)
+
+* ``PyMem_[Raw]Calloc()`` was added to the ``cpython.mem`` declarations.
+ Note that the ``Raw`` versions are no longer #defined by Cython. The previous
+ macros were not considered safe.
+ Patch by William Schwartz and David Woods. (Github issue :issue:`3047`)
+
+Bugs fixed
+----------
+
+* Circular imports of compiled modules could fail needlessly even when the import
+ could already be resolved from ``sys.modules``.
+ Patch by Syam Gadde. (Github issue :issue:`4390`)
+
+* The GIL can now safely be released inside of ``nogil`` functions (which may actually
+ be called with the GIL held at runtime).
+ Patch by David Woods. (Github issue :issue:`4137`)
+
+* Type errors when passing memory view arguments could leak buffer references.
+ Patch by David Woods. (Github issue :issue:`4296`)
+
+* Cython did not type the ``self`` argument in special binary methods.
+ Patch by David Woods. (Github issue :issue:`4434`)
+
+* An incompatibility with recent coverage.py versions was resolved.
+ Patch by David Woods. (Github issue :issue:`4440`)
+
+* Fused typed default arguments generated incorrect code.
+ Patch by David Woods. (Github issue :issue:`4413`)
+
+* ``prange`` loops generated incorrect code when ``cpp_locals`` is enabled.
+ Patch by David Woods. (Github issue :issue:`4354`)
+
+* A C-level compatibility issue with recent NumPy versions was resolved.
+ Patch by David Woods. (Github issue :issue:`4396`)
+
+* Decorators on inner functions were not evaluated in the right scope.
+ Patch by David Woods. (Github issue :issue:`4367`)
+
+* Very early errors during module initialisation could lead to crashes.
+ Patch by David Woods. (Github issue :issue:`4377`)
+
+* Fused functions were binding unnecessarily, which prevented them from being pickled.
+ Patch by David Woods. (Github issue :issue:`4370`)
+
+* Some constant tuples containing strings were not deduplicated.
+ Patch by David Woods. (Github issue :issue:`4353`)
+
+* Unsupported decorators on cdef functions were not rejected in recent releases.
+ Patch by David Woods. (Github issue :issue:`4322`)
+
+* The excess arguments in a for-in-range loop with more than 3 arguments to `range()`
+ were silently ignored.
+ Original patch by Max Bachmann. (Github issue :issue:`4550`)
+
+* Python object types were not allowed as ``->`` return type annotations.
+ Patch by Matus Valo. (Github issue :issue:`4433`)
+
+* Default values for memory views arguments were not properly supported.
+ Patch by Corentin Cadiou. (Github issue :issue:`4313`)
+
+* Templating C++ classes with memory view types lead to buggy code and is now rejected.
+ Patch by David Woods. (Github issue :issue:`3085`)
+
+* Several C++ library declarations were added and fixed.
+ Patches by Dobatymo, account-login, Jonathan Helgert, Evgeny Yakimov, GalaxySnail, Max Bachmann.
+ (Github issues :issue:`4408`, :issue:`4419`, :issue:`4410`, :issue:`4395`,
+ :issue:`4423`, :issue:`4448`, :issue:`4462`, :issue:`3293`, :issue:`4522`,
+ :issue:`2171`, :issue:`4531`)
+
+* Some compiler problems and warnings were resolved.
+ Patches by David Woods, 0dminnimda, Nicolas Pauss and others.
+ (Github issues :issue:`4317`, :issue:`4324`, :issue:`4361`, :issue:`4357`)
+
+* The ``self`` argument of static methods in .pxd files was incorrectly typed.
+ Patch by David Woods. (Github issue :issue:`3174`)
+
+* A name collision when including multiple generated API header files was resolved.
+ Patch by David Woods. (Github issue :issue:`4308`)
+
+* An endless loop in ``cython-mode.el`` was resolved.
+ Patch by Johannes Mueller. (Github issue :issue:`3218`)
+
+* ``_Py_TPFLAGS_HAVE_VECTORCALL`` was always set on extension types when using the limited API.
+ Patch by David Woods. (Github issue :issue:`4453`)
+
+* Some compatibility issues with PyPy were resolved.
+ Patches by Max Bachmann, Matti Picus.
+ (Github issues :issue:`4454`, :issue:`4477`, :issue:`4478`, :issus:`4509`, :issue:`4517`)
+
+* A compiler crash when running Cython thread-parallel from distutils was resolved.
+ (Github issue :issue:`4503`)
+
+* Includes all bug-fixes from the :ref:`0.29.26` release.
+
+Other changes
+-------------
+
+* A warning was added when ``__defaults__`` or ``__kwdefaults__`` of Cython compiled
+ functions were re-assigned, since this does not current have an effect.
+ Patch by David Woods. (Github issue :issue:`2650`)
+
+
+3.0.0 alpha 9 (2021-07-21)
+==========================
+
+Features added
+--------------
+
+* Declarations for ``libcpp.algorithms``, ``libcpp.set`` and ``libcpp.unordered_set``
+ were extended.
+ Patch by David Woods. (Github issues :issue:`4271`, :issue:`4273`)
+
+* ``cygdb`` has a new option ``--skip-interpreter`` that allows using a different
+ Python runtime than the one used to generate the debugging information.
+ Patch by Alessandro Molina. (Github issue :issue:`4186`)
+
+Bugs fixed
+----------
+
+* Several issues with the new ``cpp_locals`` directive were resolved and
+ its test coverage improved.
+ Patch by David Woods. (Github issues :issue:`4266`, :issue:`4265`)
+
+* Generated utility code for C++ conversions no longer depends on several user
+ definable directives that may make it behave incorrectly.
+ Patch by David Woods. (Github issue :issue:`4206`)
+
+* A reference counting bug in the new ``@cython.total_ordering`` decorator was fixed.
+
+* Includes all bug-fixes from the :ref:`0.29.24` release.
+
+Other changes
+-------------
+
+* Parts of the documentation were (and are being) rewritten to show the
+ Cython language syntax next to the equivalent Python syntax.
+ Patches by 0dminnimda and Matus Valo. (Github issue :issue:`4187`)
+
+
+3.0.0 alpha 8 (2021-07-02)
+==========================
+
+Features added
+--------------
+
+* A ``@cython.total_ordering`` decorator has been added to automatically
+ implement all comparison operators, similar to ``functools.total_ordering``.
+ Patch by Spencer Brown. (Github issue :issue:`2090`)
+
+* A new directive ``cpp_locals`` was added that allows local C++ variables to
+ be lazily initialised (without default constructor), thus making them behave
+ more like Python variables.
+ Patch by David Woods. (Github issue :issue:`4160`)
+
+* C++17 execution policies are supported in ``libcpp.algorithm``.
+ Patch by Ashwin Srinath. (Github issue :issue:`3790`)
+
+* New C feature flags: ``CYTHON_USE_MODULE_STATE``, ``CYTHON_USE_TYPE_SPECS``
+ Both are currently considered experimental.
+ (Github issue :issue:`3611`)
+
+* ``[...] * N`` is optimised for C integer multipliers ``N``.
+ (Github issue :issue:`3922`)
+
+Bugs fixed
+----------
+
+* The dispatch code for binary operators to special methods could run into infinite recursion.
+ Patch by David Woods. (Github issue :issue:`4172`)
+
+* Code optimisations were not applied to methods of Cython implemented C++ classes.
+ Patch by David Woods. (Github issue :issue:`4212`)
+
+* The special ``cython`` module was not always detected in PEP-484 type annotations.
+ Patch by David Woods. (Github issue :issue:`4243`)
+
+* Conversion from Python dicts to ``std::map`` was broken.
+ Patch by David Woods and Mikkel Skofelt. (Github issues :issue:`4231`, :issue:`4228`)
+
+* The exception handling annotation ``except +*`` was broken.
+ Patch by David Woods. (Github issues :issue:`3065`, :issue:`3066`)
+
+* Attribute annotations in Python classes are now ignored, because they are
+ just Python objects in a dict (as opposed to the fields of extension types).
+ Patch by David Woods. (Github issues :issue:`4196`, :issue:`4198`)
+
+* An unnecessary slow-down at import time was removed from ``Cython.Distutils``.
+ Original patch by Anthony Sottile. (Github issue :issue:`4224`)
+
+* Python modules were not automatically recompiled when only their ``.pxd`` file changed.
+ Patch by Golden Rockefeller. (Github issue :issue:`1428`)
+
+* The signature of ``PyFloat_FromString()`` in ``cpython.float`` was changed
+ to match the signature in Py3. It still has an automatic fallback for Py2.
+ (Github issue :issue:`3909`)
+
+* A compile error on MSVC was resolved.
+ Patch by David Woods. (Github issue :issue:`4202`)
+
+* A C compiler warning in PyPy3 regarding ``PyEval_EvalCode()`` was resolved.
+
+* Directives starting with ``optimization.*`` in pure Python mode were incorrectly named.
+ It should have been ``optimize.*``.
+ Patch by David Woods. (Github issue :issue:`4258`)
+
+Other changes
+-------------
+
+* Variables can no longer be declared with ``cpdef``.
+ Patch by David Woods. (Github issue :issue:`887`)
+
+* Support for the now unsupported Pyston V1 was removed in favour of Pyston V2.
+ Patch by Marius Wachtler. (Github issue :issue:`4211`)
+
+* The ``Cython.Build.BuildExecutable`` tool no longer executes the program automatically.
+ Use ``cythonrun`` for that.
+
+
+3.0.0 alpha 7 (2021-05-24)
+==========================
+
+Features added
+--------------
+
+* A ``cimport`` is now supported in pure Python code by prefixing the
+ imported module name with ``cython.cimports.``, e.g.
+ ``from cython.cimports.libc.math import sin``.
+ (GIthub issue :issue:`4190`)
+
+* ``__class_getitem__`` (`PEP-560`_) is supported for cdef classes.
+ Patch by Kmol Yuan. (Github issue :issue:`3764`)
+
+* ``__mro_entries__`` (`PEP-560`_) is supported for Python classes.
+ Patch by David Woods. (Github issue :issue:`3537`)
+
+* ``cython.array`` supports simple, non-strided views.
+ (Github issue :issue:`3775`)
+
+* Self-documenting f-strings (``=``) were implemented.
+ Patch by davfsa. (Github issue :issue:`3796`)
+
+* The destructor is now called for fields in C++ structs.
+ Patch by David Woods. (Github issue :issue:`3226`)
+
+* ``std::move()`` is now also called for temps during ``yield``.
+ Patch by Yu Feng. (Github issue :issue:`4154`)
+
+* ``asyncio.iscoroutinefunction()`` now recognises coroutine functions
+ also when compiled by Cython.
+ Patch by Pedro Marques da Luz. (Github issue :issue:`2273`)
+
+* C compiler warnings and errors are now shown in Jupyter notebooks.
+ Patch by Egor Dranischnikow. (Github issue :issue:`3751`)
+
+* ``float(…)`` is optimised for string arguments (str/bytes/bytearray).
+
+* Converting C++ containers to Python lists uses less memory allocations.
+ Patch by Max Bachmann. (Github issue :issue:`4081`)
+
+* Docstrings of ``cpdef`` enums are now copied to the enum class.
+ Patch by matham. (Github issue :issue:`3805`)
+
+* The type ``cython.Py_hash_t`` is available in Python mode.
+
+* C-API declarations for ``cpython.fileobject`` were added.
+ Patch by Zackery Spytz. (Github issue :issue:`3906`)
+
+* C-API declarations for context variables in Python 3.7 were added.
+ Original patch by Zolisa Bleki. (Github issue :issue:`2281`)
+
+* More C-API declarations for ``cpython.datetime`` were added.
+ Patch by Bluenix2. (Github issue :issue:`4128`)
+
+* A new module ``cpython.time`` was added with some low-level alternatives to
+ Python's ``time`` module.
+ Patch by Brock Mendel. (Github issue :issue:`3767`)
+
+* The value ``PyBUF_MAX_NDIM`` was added to the ``cpython.buffer`` module.
+ Patch by John Kirkham. (Github issue :issue:`3811`)
+
+* "Declaration after use" is now an error for variables.
+ Patch by David Woods. (Github issue :issue:`3976`)
+
+* More declarations for C++ string methods were added.
+
+* Cython now detects when existing output files were not previously generated
+ by itself and refuses to overwrite them. It is a common mistake to name
+ the module file of a wrapper after the library (source file) that it wraps,
+ which can lead to surprising errors when the file gets overwritten.
+
+Bugs fixed
+----------
+
+* Annotations were not exposed on annotated (data-)classes.
+ Patch by matsjoyce. (Github issue :issue:`4151`)
+
+* Inline functions and other code in ``.pxd`` files could accidentally
+ inherit the compiler directives of the ``.pyx`` file that imported them.
+ Patch by David Woods. (Github issue :issue:`1071`)
+
+* Some issues were resolved that could lead to duplicated C names.
+ Patch by David Woods. (Github issue :issue:`3716`, :issue:`3741`, :issue:`3734`)
+
+* Modules with unicode names failed to build on Windows.
+ Patch by David Woods. (Github issue :issue:`4125`)
+
+* ``ndarray.shape`` failed to compile with Pythran and recent NumPy.
+ Patch by Serge Guelton. (Github issue :issue:`3762`)
+
+* Casting to ctuples is now allowed.
+ Patch by David Woods. (Github issue :issue:`3808`)
+
+* Structs could not be instantiated with positional arguments in
+ pure Python mode.
+
+* Literal list assignments to pointer variables declared in PEP-526
+ notation failed to compile.
+
+* Nested C++ types were not usable through ctypedefs.
+ Patch by Vadim Pushtaev. (Github issue :issue:`4039`)
+
+* Overloaded C++ static methods were lost.
+ Patch by Ashwin Srinath. (Github :issue:`1851`)
+
+* Cython compiled functions always provided a ``__self__`` attribute,
+ regardless of being used as a method or not.
+ Patch by David Woods. (Github issue :issue:`4036`)
+
+* Calls to ``.__class__()`` of a known extension type failed.
+ Patch by David Woods. (Github issue :issue:`3954`)
+
+* Generator expressions in pxd-overridden ``cdef`` functions could
+ fail to compile.
+ Patch by Matus Valo. (Github issue :issue:`3477`)
+
+* A reference leak on import failures was resolved.
+ Patch by Max Bachmann. (Github issue :issue:`4056`)
+
+* A C compiler warning about unused code was resolved.
+ (Github issue :issue:`3763`)
+
+* A C compiler warning about enum value casting was resolved in GCC.
+ (Github issue :issue:`2749`)
+
+* Some C compiler warninge were resolved.
+ Patches by Max Bachmann. (Github issue :issue:`4053`, :issue:`4059`, :issue:`4054`, :issue:`4148`, :issue:`4162`)
+
+* A compile failure for C++ enums in Py3.4 / MSVC was resolved.
+ Patch by Ashwin Srinath. (Github issue :issue:`3782`)
+
+* Some C++ STL methods did not propagate exceptions.
+ Patch by Max Bachmann. (Github issue :issue:`4079`)
+
+* An unsupported C-API call in PyPy was fixed.
+ Patch by Max Bachmann. (Github issue :issue:`4055`)
+
+* The Cython ``CodeWriter`` mishandled no-argument ``return`` statements.
+ Patch by Tao He. (Github issue :issue:`3795`)
+
+* ``complex`` wasn't supported in PEP-484 type annotations.
+ Patch by David Woods. (Github issue :issue:`3949`)
+
+* Default arguments of methods were not exposed for introspection.
+ Patch by Vladimir Matveev. (Github issue :issue:`4061`)
+
+* Extension types inheriting from Python classes could not safely
+ be exposed in ``.pxd`` files.
+ (Github issue :issue:`4106`)
+
+* The profiling/tracing code was adapted to work with Python 3.10b1.
+
+* The internal CPython macro ``Py_ISSPACE()`` is no longer used.
+ Original patch by Andrew Jones. (Github issue :issue:`4111`)
+
+* Includes all bug-fixes from the :ref:`0.29.23` release.
+
+
+3.0.0 alpha 6 (2020-07-31)
+==========================
+
+Features added
+--------------
+
+* Special methods for binary operators now follow Python semantics.
+ Rather than e.g. a single ``__add__`` method for cdef classes, where
+ "self" can be either the first or second argument, one can now define
+ both ``__add__`` and ``__radd__`` as for standard Python classes.
+ This behavior can be disabled with the ``c_api_binop_methods`` directive
+ to return to the previous semantics in Cython code (available from Cython
+ 0.29.20), or the reversed method (``__radd__``) can be implemented in
+ addition to an existing two-sided operator method (``__add__``) to get a
+ backwards compatible implementation.
+ (Github issue :issue:`2056`)
+
+* No/single argument functions now accept keyword arguments by default in order
+ to comply with Python semantics. The marginally faster calling conventions
+ ``METH_NOARGS`` and ``METH_O`` that reject keyword arguments are still available
+ with the directive ``@cython.always_allow_keywords(False)``.
+ (Github issue :issue:`3090`)
+
+* For-in-loop iteration over ``bytearray`` and memory views is optimised.
+ Patch by David Woods. (Github issue :issue:`2227`)
+
+* Type inference now works for memory views and slices.
+ Patch by David Woods. (Github issue :issue:`2227`)
+
+* The ``@returns()`` decorator propagates exceptions by default for suitable C
+ return types when no ``@exceptval()`` is defined.
+ (Github issues :issue:`3625`, :issue:`3664`)
+
+* A low-level inline function ``total_seconds(timedelta)`` was added to
+ ``cpython.datetime`` to bypass the Python method call. Note that this function
+ is not guaranteed to give exactly the same results for very large time intervals.
+ Patch by Brock Mendel. (Github issue :issue:`3616`)
+
+* Type inference now understands that ``a, *b = x`` assigns a list to ``b``.
+
+* Limited API support was improved.
+ Patches by Matthias Braun. (Github issues :issue:`3693`, :issue:`3707`)
+
+* The Cython ``CodeWriter`` can now handle more syntax constructs.
+ Patch by Tao He. (Github issue :issue:`3514`)
+
+Bugs fixed
+----------
+
+* The construct ``for x in cpp_function_call()`` failed to compile.
+ Patch by David Woods. (Github issue :issue:`3663`)
+
+* C++ references failed to compile when used as Python object indexes.
+ Patch by David Woods. (Github issue :issue:`3754`)
+
+* The C++ ``typeid()`` function was allowed in C mode.
+ Patch by Celelibi. (Github issue :issue:`3637`)
+
+* ``repr()`` was assumed to return ``str`` instead of ``unicode`` with ``language_level=3``.
+ (Github issue :issue:`3736`)
+
+* Includes all bug-fixes from the :ref:`0.29.21` release.
+
+Other changes
+-------------
+
+* The ``numpy`` declarations were updated.
+ Patch by Brock Mendel. (Github issue :issue:`3630`)
+
+* The names of Cython's internal types (functions, generator, coroutine, etc.)
+ are now qualified with the module name of the internal Cython module that is
+ used for sharing them across Cython implemented modules, for example
+ ``_cython_3_0a5.coroutine``. This was done to avoid making them look like
+ homeless builtins, to help with debugging, and in order to avoid a CPython
+ warning according to https://bugs.python.org/issue20204
+
+3.0.0 alpha 5 (2020-05-19)
+==========================
+
+Features added
+--------------
+
+* ``.pxd`` files can now be :ref:`versioned ` by adding an
+ extension like "``.cython-30.pxd``" to prevent older Cython versions (than
+ 3.0 in this case) from picking them up. (Github issue :issue:`3577`)
+
+* Several macros/functions declared in the NumPy API are now usable without
+ holding the GIL.
+
+* `libc.math` was extended to include all C99 function declarations.
+ Patch by Dean Scarff. (Github issue :issue:`3570`)
+
+Bugs fixed
+----------
+
+* Several issues with arithmetic overflow handling were resolved, including
+ undefined behaviour in C.
+ Patch by Sam Sneddon. (Github issue :issue:`3588`)
+
+* The improved GIL handling in ``nogil`` functions introduced in 3.0a3
+ could fail to acquire the GIL in some cases on function exit.
+ (Github issue :issue:`3590` etc.)
+
+* A reference leak when processing keyword arguments in Py2 was resolved,
+ that appeared in 3.0a1.
+ (Github issue :issue:`3578`)
+
+* The outdated getbuffer/releasebuffer implementations in the NumPy
+ declarations were removed so that buffers declared as ``ndarray``
+ now use the normal implementation in NumPy.
+
+* Includes all bug-fixes from the :ref:`0.29.18` release.
+
+
+3.0.0 alpha 4 (2020-05-05)
+==========================
+
+Features added
+--------------
+
+* The ``print`` statement (not the ``print()`` function) is allowed in
+ ``nogil`` code without an explicit ``with gil`` section.
+
+* The ``assert`` statement is allowed in ``nogil`` sections. Here, the GIL is
+ only acquired if the ``AssertionError`` is really raised, which means that the
+ evaluation of the asserted condition only allows C expressions.
+
+* Cython generates C compiler branch hints for unlikely user defined if-clauses
+ in more cases, when they end up raising exceptions unconditionally. This now
+ includes exceptions being raised in ``nogil``/``with gil`` sections.
+
+* Some internal memoryview functions were tuned to reduce object overhead.
+
+Bugs fixed
+----------
+
+* Exception position reporting could run into race conditions on threaded code.
+ It now uses function-local variables again.
+
+* Error handling early in the module init code could lead to a crash.
+
+* Error handling in ``cython.array`` creation was improved to avoid calling
+ C-API functions with an error held.
+
+* Complex buffer item types of structs of arrays could fail to validate.
+ Patch by Leo and smutch. (Github issue :issue:`1407`)
+
+* When importing the old Cython ``build_ext`` integration with distutils, the
+ additional command line arguments leaked into the regular command.
+ Patch by Kamekameha. (Github issue :issue:`2209`)
+
+* The improved GIL handling in ``nogil`` functions introduced in 3.0a3
+ could generate invalid C code.
+ (Github issue :issue:`3558`)
+
+* ``PyEval_InitThreads()`` is no longer used in Py3.7+ where it is a no-op.
+
+* Parallel builds of Cython itself (``setup.py build_ext -j N``) failed on Windows.
+
+Other changes
+-------------
+
+* The C property feature has been rewritten and now requires C property methods
+ to be declared ``inline`` (:issue:`3571`).
+
+
+3.0.0 alpha 3 (2020-04-27)
+==========================
+
+Features added
+--------------
+
+* ``nogil`` functions now avoid acquiring the GIL on function exit if possible
+ even if they contain ``with gil`` blocks.
+ (Github issue :issue:`3554`)
+
+* Python private name mangling now falls back to unmangled names for non-Python
+ globals, since double-underscore names are not uncommon in C. Unmangled Python
+ names are also still found as a legacy fallback but produce a warning.
+ Patch by David Woods. (Github issue :issue:`3548`)
+
+Bugs fixed
+----------
+
+* Includes all bug-fixes from the :ref:`0.29.17` release.
+
+
+3.0.0 alpha 2 (2020-04-23)
+==========================
+
+Features added
+--------------
+
+* ``std::move()`` is now used in C++ mode for internal temp variables to
+ make them work without copying values.
+ Patch by David Woods. (Github issues :issue:`3253`, :issue:`1612`)
+
+* ``__class_getitem__`` is supported for types on item access (`PEP-560`_).
+ Patch by msg555. (Github issue :issue:`2753`)
+
+* The simplified Py3.6 customisation of class creation is implemented (`PEP-487`_).
+ (Github issue :issue:`2781`)
+
+* Conditional blocks in Python code that depend on ``cython.compiled`` are
+ eliminated at an earlier stage, which gives more freedom in writing
+ replacement Python code.
+ Patch by David Woods. (Github issue :issue:`3507`)
+
+* ``numpy.import_array()`` is automatically called if ``numpy`` has been cimported
+ and it has not been called in the module code. This is intended as a hidden
+ fail-safe so user code should continue to call ``numpy.import_array``.
+ Patch by David Woods. (Github issue :issue:`3524`)
+
+* The Cython AST code serialiser class ``CodeWriter`` in ``Cython.CodeWriter``
+ supports more syntax nodes.
+
+* The fastcall/vectorcall protocols are used for several internal Python calls.
+ (Github issue :issue:`3540`)
+
+Bugs fixed
+----------
+
+* With ``language_level=3/3str``, Python classes without explicit base class
+ are now new-style (type) classes also in Py2. Previously, they were created
+ as old-style (non-type) classes.
+ (Github issue :issue:`3530`)
+
+* C++ ``typeid()`` failed for fused types.
+ Patch by David Woods. (Github issue :issue:`3203`)
+
+* ``__arg`` argument names in methods were not mangled with the class name.
+ Patch by David Woods. (Github issue :issue:`1382`)
+
+* Creating an empty unicode slice with large bounds could crash.
+ Patch by Sam Sneddon. (Github issue :issue:`3531`)
+
+* Decoding an empty bytes/char* slice with large bounds could crash.
+ Patch by Sam Sneddon. (Github issue :issue:`3534`)
+
+* Temporary buffer indexing variables were not released and could show up in
+ C compiler warnings, e.g. in generators.
+ Patch by David Woods. (Github issues :issue:`3430`, :issue:`3522`)
+
+* Several C compiler warnings were fixed.
+
+
+3.0.0 alpha 1 (2020-04-12)
+==========================
+
+Features added
+--------------
+
+* Cython functions now use the `PEP-590`_ vectorcall protocol in Py3.7+.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2263`)
+
+* Unicode identifiers are supported in Cython code (`PEP-3131`_).
+ Patch by David Woods. (Github issue :issue:`2601`)
+
+* Unicode module names and imports are supported.
+ Patch by David Woods. (Github issue :issue:`3119`)
+
+* Annotations are no longer parsed, keeping them as strings following `PEP-563`_.
+ Patch by David Woods. (Github issue :issue:`3285`)
+
+* Preliminary support for the CPython's ``Py_LIMITED_API`` (stable ABI) is
+ available by setting the ``CYTHON_LIMITED_API`` C macro. Note that the
+ support is currently in an early stage and many features do not yet work.
+ You currently still have to define ``Py_LIMITED_API`` externally in order
+ to restrict the API usage. This will change when the feature stabilises.
+ Patches by Eddie Elizondo and David Woods. (Github issues :issue:`3223`,
+ :issue:`3311`, :issue:`3501`)
+
+* The dispatch to fused functions is now linear in the number of arguments,
+ which makes it much faster, often 2x or more, and several times faster for
+ larger fused types with many specialisations.
+ Patch by will-ca. (Github issue :issue:`1385`)
+
+* ``with gil/nogil`` statements can be conditional based on compile-time
+ constants, e.g. fused type checks.
+ Patch by Noam Hershtig. (Github issue :issue:`2579`)
+
+* ``const`` can be used together with fused types.
+ Patch by Thomas Vincent. (Github issue :issue:`1772`)
+
+* Reimports of already imported modules are substantially faster.
+ (Github issue :issue:`2854`)
+
+* Positional-only arguments are supported in Python functions (`PEP-570`_).
+ Patch by Josh Tobin. (Github issue :issue:`2915`)
+
+* The ``volatile`` C modifier is supported in Cython code.
+ Patch by Jeroen Demeyer. (Github issue :issue:`1667`)
+
+* ``@cython.trashcan(True)`` can be used on an extension type to enable the
+ CPython :ref:`trashcan`. This allows deallocating deeply recursive objects
+ without overflowing the stack. Patch by Jeroen Demeyer. (Github issue :issue:`2842`)
+
+* Inlined properties can be defined for external extension types.
+ Patch by Matti Picus. (Github issue :issue:`2640`, redone later in :issue:`3571`)
+
+* The ``str()`` builtin now calls ``PyObject_Str()`` instead of going
+ through a Python call.
+ Patch by William Ayd. (Github issue :issue:`3279`)
+
+* String concatenation can now happen in place if possible, by extending the
+ existing string rather than always creating a new one.
+ Patch by David Woods. (Github issue :issue:`3453`)
+
+* Multiplication of Python numbers with small constant integers is faster.
+ (Github issue :issue:`2808`)
+
+* Some list copying is avoided internally when a new list needs to be created
+ but we already have a fresh one.
+ (Github issue :issue:`3494`)
+
+* Extension types that do not need their own ``tp_new`` implementation (because
+ they have no object attributes etc.) directly inherit the implementation of
+ their parent type if possible.
+ (Github issue :issue:`1555`)
+
+* The attributes ``gen.gi_frame`` and ``coro.cr_frame`` of Cython compiled
+ generators and coroutines now return an actual frame object for introspection.
+ (Github issue :issue:`2306`)
+
+* Several declarations in ``cpython.*``, ``libc.*`` and ``libcpp.*`` were added.
+ Patches by Jeroen Demeyer, Matthew Edwards, Chris Gyurgyik, Jerome Kieffer
+ and Zackery Spytz.
+ (Github issues :issue:`3468`, :issue:`3332`, :issue:`3202`, :issue:`3188`,
+ :issue:`3179`, :issue:`2891`, :issue:`2826`, :issue:`2713`)
+
+* Deprecated NumPy API usages were removed from ``numpy.pxd``.
+ Patch by Matti Picus. (Github issue :issue:`3365`)
+
+* ``cython.inline()`` now sets the ``NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION``
+ C macro automatically when ``numpy`` is imported in the code, to avoid C compiler
+ warnings about deprecated NumPy C-API usage.
+
+* The builtin ``abs()`` function can now be used on C numbers in nogil code.
+ Patch by Elliott Sales de Andrade. (Github issue :issue:`2748`)
+
+* `PEP-479`_ (``generator_stop``) is now enabled by default with language level 3.
+ (Github issue :issue:`2580`)
+
+* The ``cython.view.array`` type supports inheritance.
+ Patch by David Woods. (Github issue :issue:`3413`)
+
+* Code annotation accepts a new debugging argument ``--annotate-fullc`` that
+ will include the complete syntax highlighted C file in the HTML output.
+ (Github issue :issue:`2855`)
+
+* ``--no-capture`` added to ``runtests.py`` to prevent stdout/stderr capturing
+ during srctree tests.
+ Patch by Matti Picus. (Github issue :issue:`2701`)
+
+* ``--no-docstrings`` option added to ``cythonize`` script.
+ Original patch by mo-han. (Github issue :issue:`2889`)
+
+* ``cygdb`` gives better error messages when it fails to initialise the
+ Python runtime support in gdb.
+ Patch by Volker Weissmann. (Github issue :issue:`3489`)
+
+* The Pythran ``shape`` attribute is supported.
+ Patch by Serge Guelton. (Github issue :issue:`3307`)
+
+Bugs fixed
+----------
+
+* The unicode methods ``.upper()``, ``.lower()`` and ``.title()`` were
+ incorrectly optimised for single character input values and only returned
+ the first character if multiple characters should have been returned.
+ They now use the original Python methods again.
+
+* Fused argument types were not correctly handled in type annotations and
+ ``cython.locals()``.
+ Patch by David Woods. (Github issues :issue:`3391`, :issue:`3142`)
+
+* Diverging from the usual behaviour, ``len(memoryview)``, ``len(char*)``
+ and ``len(Py_UNICODE*)`` returned an unsigned ``size_t`` value. They now
+ return a signed ``Py_ssize_t``, like other usages of ``len()``.
+
+* Nested dict literals in function call kwargs could incorrectly raise an
+ error about duplicate keyword arguments, which are allowed when passing
+ them from dict literals.
+ (Github issue :issue:`2963`)
+
+* Item access (subscripting) with integer indices/keys always tried the
+ Sequence protocol before the Mapping protocol, which diverged from Python
+ semantics. It now passes through the Mapping protocol first when supported.
+ (Github issue :issue:`1807`)
+
+* Name lookups in class bodies no longer go through an attribute lookup.
+ Patch by Jeroen Demeyer. (Github issue :issue:`3100`)
+
+* Broadcast assignments to a multi-dimensional memory view slice could end
+ up in the wrong places when the underlying memory view is known to be
+ contiguous but the slice is not.
+ (Github issue :issue:`2941`)
+
+* Pickling unbound methods of Python classes failed.
+ Patch by Pierre Glaser. (Github issue :issue:`2972`)
+
+* The ``Py_hash_t`` type failed to accept arbitrary "index" values.
+ (Github issue :issue:`2752`)
+
+* The first function line number of functions with decorators pointed to the
+ signature line and not the first decorator line, as in Python.
+ Patch by Felix Kohlgrüber. (Github issue :issue:`2536`)
+
+* Constant integer expressions that used a negative exponent were evaluated
+ as integer 0 instead of the expected float value.
+ Patch by Kryštof Pilnáček. (Github issue :issue:`2133`)
+
+* The ``cython.declare()`` and ``cython.cast()`` functions could fail in pure mode.
+ Patch by Dmitry Shesterkin. (Github issue :issue:`3244`)
+
+* ``__doc__`` was not available inside of the class body during class creation.
+ (Github issue :issue:`1635`)
+
+* Setting ``language_level=2`` in a file did not work if ``language_level=3``
+ was enabled globally before.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2791`)
+
+* ``__init__.pyx`` files were not always considered as package indicators.
+ (Github issue :issue:`2665`)
+
+* Compiling package ``__init__`` files could fail under Windows due to an
+ undefined export symbol. (Github issue :issue:`2968`)
+
+* A C compiler cast warning was resolved.
+ Patch by Michael Buesch. (Github issue :issue:`2775`)
+
+* Binding staticmethods of Cython functions were not behaving like Python methods.
+ Patch by Jeroen Demeyer. (Github issue :issue:`3106`, :issue:`3102`)
+
+* Memoryviews failed to compile when the ``cache_builtins`` feature was disabled.
+ Patch by David Woods. (Github issue :issue:`3406`)
+
+Other changes
+-------------
+
+* The default language level was changed to ``3str``, i.e. Python 3 semantics,
+ but with ``str`` literals (also in Python 2.7). This is a backwards incompatible
+ change from the previous default of Python 2 semantics. The previous behaviour
+ is available through the directive ``language_level=2``.
+ (Github issue :issue:`2565`)
+
+* Cython no longer generates ``__qualname__`` attributes for classes in Python
+ 2.x since they are problematic there and not correctly maintained for subclasses.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2772`)
+
+* Source file fingerprinting now uses SHA-1 instead of MD5 since the latter
+ tends to be slower and less widely supported these days.
+ (Github issue :issue:`2790`)
+
+* The long deprecated include files ``python_*``, ``stdio``, ``stdlib`` and
+ ``stl`` in ``Cython/Includes/Deprecated/`` were removed. Use the ``libc.*``
+ and ``cpython.*`` pxd modules instead.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2904`)
+
+* The search order for include files was changed. Previously it was
+ ``include_directories``, ``Cython/Includes``, ``sys.path``. Now it is
+ ``include_directories``, ``sys.path``, ``Cython/Includes``. This was done to
+ allow third-party ``*.pxd`` files to override the ones in Cython.
+ Patch by Matti Picus. (Github issue :issue:`2905`)
+
+* The command line parser was rewritten and modernised using ``argparse``.
+ Patch by Egor Dranischnikow. (Github issue :issue:`2952`, :issue:`3001`)
+
+* Dotted filenames for qualified module names (``pkg.mod.pyx``) are deprecated.
+ Use the normal Python package directory layout instead.
+ (Github issue :issue:`2686`)
+
+* Binary Linux wheels now follow the manylinux2010 standard.
+ Patch by Alexey Stepanov. (Github issue :issue:`3355`)
+
+* Support for Python 2.6 was removed.
+
+.. _`PEP-560`: https://www.python.org/dev/peps/pep-0560
+.. _`PEP-570`: https://www.python.org/dev/peps/pep-0570
+.. _`PEP-487`: https://www.python.org/dev/peps/pep-0487
+.. _`PEP-590`: https://www.python.org/dev/peps/pep-0590
+.. _`PEP-3131`: https://www.python.org/dev/peps/pep-3131
+.. _`PEP-563`: https://www.python.org/dev/peps/pep-0563
+.. _`PEP-479`: https://www.python.org/dev/peps/pep-0479
+
+
+.. _0.29.28:
+
+0.29.28 (2022-02-17)
+====================
+
+Bugs fixed
+----------
+
+* Due to backwards incompatible changes in CPython 3.11a4, the feature flags
+ ``CYTHON_FAST_THREAD_STATE`` and ``CYTHON_USE_EXC_INFO_STACK`` are now disabled
+ in Python 3.11 and later. They are enabled again in Cython 3.0.
+ Patch by David Woods. (Github issue #4610)
+
+* A C compiler warning in older PyPy versions was resolved.
+ Patch by Matti Picus. (Github issue #4236)
+
+
+.. _0.29.27:
+
+0.29.27 (2022-01-28)
+====================
+
+Features added
+--------------
+
+* The ``cythonize`` command has a new option ``-M`` to generate ``.dep`` dependency
+ files for the compilation unit. This can be used by external build tools to track
+ these dependencies.
+ Patch by Evgeni Burovski. (Github issue :issue:`1214`)
+
+Bugs fixed
+----------
+
+* Compilation failures on PyPy were resolved.
+ Patches by Matti Picus. (Github issues :issue:`4509`, :issue:`4517`)
+
+* Calls to ``range()`` with more than three arguments did not fail.
+ Original patch by Max Bachmann. (Github issue :issue:`4550`)
+
+* Some C compiler warnings about missing type struct initialisers in Py3.10 were resolved.
+
+* Cython no longer warns about using OpenMP 3.0 features since they are now
+ considered generally available.
+
+
+.. _0.29.26:
+
+0.29.26 (2021-12-16)
+====================
+
+Bugs fixed
+----------
+
+* An incompatibility with CPython 3.11.0a3 was resolved.
+ (Github issue :issue:`4499`)
+
+* The ``in`` operator failed on literal lists with starred expressions.
+ Patch by Arvind Natarajan. (Github issue :issue:`3938`)
+
+* A C compiler warning in PyPy about a missing struct field initialisation was resolved.
+
+
+.. _0.29.25:
+
+0.29.25 (2021-12-06)
+====================
+
+Bugs fixed
+----------
+
+* Several incompatibilities with CPython 3.11 were resolved.
+ Patches by David Woods, Victor Stinner, Thomas Caswell.
+ (Github issues :issue:`4411`, :issue:`4414`, :issue:`4415`, :issue:`4416`, :issue:`4420`,
+ :issue:`4428`, :issue:`4473`, :issue:`4479`, :issue:`4480`)
+
+* Some C compiler warnings were resolved.
+ Patches by Lisandro Dalcin and others. (Github issue :issue:`4439`)
+
+* C++ ``std::move()`` should only be used automatically in MSVC versions that support it.
+ Patch by Max Bachmann. (Github issue :issue:`4191`)
+
+ * The ``Py_hash_t`` type failed to accept arbitrary "index" values.
+ (Github issue :issue:`2752`)
+
+* Avoid copying unaligned 16-bit values since some platforms require them to be aligned.
+ Use memcpy() instead to let the C compiler decide how to do it.
+ (Github issue :issue:`4343`)
+
+* Cython crashed on invalid truthiness tests on C++ types without ``operator bool``.
+ Patch by David Woods. (Github issue :issue:`4348`)
+
+* The declaration of ``PyUnicode_CompareWithASCIIString()`` in ``cpython.unicode`` was incorrect.
+ Patch by Max Bachmann. (Github issue :issue:`4344`)
+
+
+.. _0.29.24:
+
+0.29.24 (2021-07-14)
+====================
+
+Bugs fixed
+----------
+
+* Inline functions in pxd files that used memory views could lead to invalid
+ C code if the module that imported from them does not use memory views.
+ Patch by David Woods. (Github issue :issue:`1415`)
+
+* Several declarations in ``libcpp.string`` were added and corrected.
+ Patch by Janek Bevendorff. (Github issue :issue:`4268`)
+
+* Pickling unbound Cython compiled methods failed.
+ Patch by Pierre Glaser. (Github issue :issue:`2972`)
+
+* The tracing code was adapted to work with CPython 3.10.
+
+* The optimised ``in`` operator failed on unicode strings in Py3.9 and later
+ that were constructed from an external ``wchar_t`` source.
+ Also, related C compiler warnings about deprecated C-API usage were resolved.
+ (Github issue :issue:`3925`)
+
+* Some compiler crashes were resolved.
+ Patch by David Woods. (Github issues :issue:`4214`, :issue:`2811`)
+
+* An incorrect warning about 'unused' generator expressions was removed.
+ (GIthub issue :issue:`1699`)
+
+* The attributes ``gen.gi_frame`` and ``coro.cr_frame`` of Cython compiled
+ generators and coroutines now return an actual frame object for introspection,
+ instead of ``None``.
+ (Github issue :issue:`2306`)
+
+
+.. _0.29.23:
+
+0.29.23 (2021-04-14)
+====================
+
+Bugs fixed
+----------
+
+* Some problems with Python 3.10 were resolved.
+ Patches by Victor Stinner and David Woods. (Github issues :issue:`4046`, :issue:`4100`)
+
+* An incorrect "optimisation" was removed that allowed changes to a keyword
+ dict to leak into keyword arguments passed into a function.
+ Patch by Peng Weikang. (Github issue :issue:`3227`)
+
+* Multiplied str constants could end up as bytes constants with language_level=2.
+ Patch by Alphadelta14 and David Woods. (Github issue :issue:`3951`)
+
+* ``PY_SSIZE_T_CLEAN`` does not get defined any more if it is already defined.
+ Patch by Andrew Jones. (Github issue :issue:`4104`)
+
+
+.. _0.29.22:
+
+0.29.22 (2021-02-20)
+====================
+
+Features added
+--------------
+
+* Some declarations were added to the provided pxd includes.
+ Patches by Zackery Spytz and John Kirkham.
+ (Github issues :issue:`3811`, :issue:`3882`, :issue:`3899`, :issue:`3901`)
+
+Bugs fixed
+----------
+
+* A crash when calling certain functions in Py3.9 and later was resolved.
+ (Github issue :issue:`3917`)
+
+* ``const`` memory views of structs failed to compile.
+ (Github issue :issue:`2251`)
+
+* ``const`` template declarations could not be nested.
+ Patch by Ashwin Srinath. (Github issue :issue:`1355`)
+
+* The declarations in the ``cpython.pycapsule`` module were missing their
+ ``const`` modifiers and generated incorrect C code.
+ Patch by Warren Weckesser. (Github issue :issue:`3964`)
+
+* Casts to memory views failed for fused dtypes.
+ Patch by David Woods. (Github issue :issue:`3881`)
+
+* ``repr()`` was assumed to return ``str`` instead of ``unicode`` with ``language_level=3``.
+ (Github issue :issue:`3736`)
+
+* Calling ``cpdef`` functions from cimported modules crashed the compiler.
+ Patch by David Woods. (Github issue :issue:`4000`)
+
+* Cython no longer validates the ABI size of the NumPy classes it compiled against.
+ See the discussion in https://github.com/numpy/numpy/pull/432
+
+* A C compiler warning about enum value casting was resolved in GCC.
+ (Github issue :issue:`2749`)
+
+* Coverage reporting in the annotated HTML file failed in Py3.9.
+ Patch by Nick Pope. (Github issue :issue:`3865`)
+
+* The embedding code now reports Python errors as exit status.
+
+* Long type declarations could lead to (harmless) random changes in the
+ C file when used in auto-generated Python wrappers or pickled classes.
+
+Other changes
+-------------
+
+* Variables defined as ``cpdef`` now generate a warning since this
+ is currently useless and thus does not do what users would expect.
+ Patch by David Woods. (Github issue :issue:`3959`)
+
+
+.. _0.29.21:
+
+0.29.21 (2020-07-09)
+====================
+
+Bugs fixed
+----------
+
+* Fix a regression in 0.29.20 where ``__div__`` failed to be found in extension types.
+ (Github issue :issue:`3688`)
+
+* Fix a regression in 0.29.20 where a call inside of a finally clause could fail to compile.
+ Patch by David Woods. (Github issue :issue:`3712`)
+
+* Zero-sized buffers could fail to validate as C/Fortran-contiguous.
+ Patch by Clemens Hofreither. (Github issue :issue:`2093`)
+
+* ``exec()`` did not allow recent Python syntax features in Py3.8+ due to
+ https://bugs.python.org/issue35975.
+ (Github issue :issue:`3695`)
+
+* Binding staticmethods of Cython functions were not behaving like Python methods in Py3.
+ Patch by Jeroen Demeyer and Michał Górny. (Github issue :issue:`3106`)
+
+* Pythran calls to NumPy methods no longer generate useless method lookup code.
+
+* The ``PyUnicode_GET_LENGTH()`` macro was missing from the ``cpython.*`` declarations.
+ Patch by Thomas Caswell. (Github issue :issue:`3692`)
+
+* The deprecated ``PyUnicode_*()`` C-API functions are no longer used, except for Unicode
+ strings that contain lone surrogates. Unicode strings that contain non-BMP characters
+ or surrogate pairs now generate different C code on 16-bit Python 2.x Unicode deployments
+ (such as MS-Windows). Generating the C code on Python 3.x is recommended in this case.
+ Original patches by Inada Naoki and Victor Stinner.
+ (Github issues :issue:`3677`, :issue:`3721`, :issue:`3697`)
+
+* Some template parameters were missing from the C++ ``std::unordered_map`` declaration.
+ Patch by will. (Github issue :issue:`3685`)
+
+* Several internal code generation issues regarding temporary variables were resolved.
+ (Github issue :issue:`3708`)
+
+
+.. _0.29.20:
+
+0.29.20 (2020-06-10)
+====================
+
+Bugs fixed
+----------
+
+* Nested try-except statements with multiple ``return`` statements could crash
+ due to incorrect deletion of the ``except as`` target variable.
+ (Github issue :issue:`3666`)
+
+* The ``@classmethod`` decorator no longer rejects unknown input from other decorators.
+ Patch by David Woods. (Github issue :issue:`3660`)
+
+* Fused types could leak into unrelated usages.
+ Patch by David Woods. (Github issue :issue:`3642`)
+
+* Now uses ``Py_SET_SIZE()`` and ``Py_SET_REFCNT()`` in Py3.9+ to avoid low-level
+ write access to these object fields.
+ Patch by Victor Stinner. (Github issue :issue:`3639`)
+
+* The built-in ``abs()`` function could lead to undefined behaviour when used on
+ the negative-most value of a signed C integer type.
+ Patch by Serge Guelton. (Github issue :issue:`1911`)
+
+* Usages of ``sizeof()`` and ``typeid()`` on uninitialised variables no longer
+ produce a warning.
+ Patch by Celelibi. (Github issue :issue:`3575`)
+
+* The C++ ``typeid()`` function was allowed in C mode.
+ Patch by Celelibi. (Github issue :issue:`3637`)
+
+* The error position reported for errors found in f-strings was misleading.
+ (Github issue :issue:`3674`)
+
+* The new ``c_api_binop_methods`` directive was added for forward compatibility, but can
+ only be set to True (the current default value). It can be disabled in Cython 3.0.
+
+
+.. _0.29.19:
+
+0.29.19 (2020-05-20)
+====================
+
+Bugs fixed
+----------
+
+* A typo in Windows specific code in 0.29.18 was fixed that broke "libc.math".
+ (Github issue :issue:`3622`)
+
+* A platform specific test failure in 0.29.18 was fixed.
+ Patch by smutch. (Github issue :issue:`3620`)
+
+
+.. _0.29.18:
+
+0.29.18 (2020-05-18)
+====================
+
+Bugs fixed
+----------
+
+* Exception position reporting could run into race conditions on threaded code.
+ It now uses function-local variables again.
+
+* Error handling early in the module init code could lead to a crash.
+
+* Error handling in ``cython.array`` creation was improved to avoid calling
+ C-API functions with an error held.
+
+* A memory corruption was fixed when garbage collection was triggered during calls
+ to ``PyType_Ready()`` of extension type subclasses.
+ (Github issue :issue:`3603`)
+
+* Memory view slicing generated unused error handling code which could negatively
+ impact the C compiler optimisations for parallel OpenMP code etc. Also, it is
+ now helped by static branch hints.
+ (Github issue :issue:`2987`)
+
+* Cython's built-in OpenMP functions were not translated inside of call arguments.
+ Original patch by Celelibi and David Woods. (Github issue :issue:`3594`)
+
+* Complex buffer item types of structs of arrays could fail to validate.
+ Patch by Leo and smutch. (Github issue :issue:`1407`)
+
+* Decorators were not allowed on nested `async def` functions.
+ (Github issue :issue:`1462`)
+
+* C-tuples could use invalid C struct casting.
+ Patch by MegaIng. (Github issue :issue:`3038`)
+
+* Optimised ``%d`` string formatting into f-strings failed on float values.
+ (Github issue :issue:`3092`)
+
+* Optimised aligned string formatting (``%05s``, ``%-5s``) failed.
+ (Github issue :issue:`3476`)
+
+* When importing the old Cython ``build_ext`` integration with distutils, the
+ additional command line arguments leaked into the regular command.
+ Patch by Kamekameha. (Github issue :issue:`2209`)
+
+* When using the ``CYTHON_NO_PYINIT_EXPORT`` option in C++, the module init function
+ was not declared as ``extern "C"``.
+ (Github issue :issue:`3414`)
+
+* Three missing timedelta access macros were added in ``cpython.datetime``.
+
+* The signature of the NumPy C-API function ``PyArray_SearchSorted()`` was fixed.
+ Patch by Brock Mendel. (Github issue :issue:`3606`)
+
+
+.. _0.29.17:
+
+0.29.17 (2020-04-26)
+====================
+
+Features added
+--------------
+
+* ``std::move()`` is now available from ``libcpp.utility``.
+ Patch by Omer Ozarslan. (Github issue :issue:`2169`)
+
+* The ``@cython.binding`` decorator is available in Python code.
+ (Github issue :issue:`3505`)
+
+Bugs fixed
+----------
+
+* Creating an empty unicode slice with large bounds could crash.
+ Patch by Sam Sneddon. (Github issue :issue:`3531`)
+
+* Decoding an empty bytes/char* slice with large bounds could crash.
+ Patch by Sam Sneddon. (Github issue :issue:`3534`)
+
+* Re-importing a Cython extension no longer raises the error
+ "``__reduce_cython__ not found``".
+ (Github issue :issue:`3545`)
+
+* Unused C-tuples could generate incorrect code in 0.29.16.
+ Patch by Kirk Meyer. (Github issue :issue:`3543`)
+
+* Creating a fused function attached it to the garbage collector before it
+ was fully initialised, thus risking crashes in rare failure cases.
+ Original patch by achernomorov. (Github issue :issue:`3215`)
+
+* Temporary buffer indexing variables were not released and could show up in
+ C compiler warnings, e.g. in generators.
+ Patch by David Woods. (Github issues :issue:`3430`, :issue:`3522`)
+
+* The compilation cache in ``cython.inline("…")`` failed to take the language
+ level into account.
+ Patch by will-ca. (Github issue :issue:`3419`)
+
+* The deprecated ``PyUnicode_GET_SIZE()`` function is no longer used in Py3.
+
+
+.. _0.29.16:
+
+0.29.16 (2020-03-24)
+====================
+
+Bugs fixed
+----------
+
+* Temporary internal variables in nested prange loops could leak into other
+ threads. Patch by Frank Schlimbach. (Github issue :issue:`3348`)
+
+* Default arguments on fused functions could crash.
+ Patch by David Woods. (Github issue :issue:`3370`)
+
+* C-tuples declared in ``.pxd`` files could generate incomplete C code.
+ Patch by Kirk Meyer. (Github issue :issue:`1427`)
+
+* Fused functions were not always detected and optimised as Cython
+ implemented functions.
+ Patch by David Woods. (Github issue :issue:`3384`)
+
+* Valid Python object concatenation of (iterable) strings to non-strings
+ could fail with an exception.
+ Patch by David Woods. (Github issue :issue:`3433`)
+
+* Using C functions as temporary values lead to invalid C code.
+ Original patch by David Woods. (Github issue :issue:`3418`)
+
+* Fix an unhandled C++ exception in comparisons.
+ Patch by David Woods. (Github issue :issue:`3361`)
+
+* Fix deprecated import of "imp" module.
+ Patch by Matti Picus. (Github issue :issue:`3350`)
+
+* Fix compatibility with Pythran 0.9.6 and later.
+ Patch by Serge Guelton. (Github issue :issue:`3308`)
+
+* The ``_Py_PyAtExit()`` function in ``cpython.pylifecycle`` was misdeclared.
+ Patch by Zackery Spytz. (Github issue :issue:`3382`)
+
+* Several missing declarations in ``cpython.*`` were added.
+ Patches by Zackery Spytz. (Github issue :issue:`3452`, :issue:`3421`, :issue:`3411`, :issue:`3402`)
+
+* A declaration for ``libc.math.fpclassify()`` was added.
+ Patch by Zackery Spytz. (Github issue :issue:`2514`)
+
+* Avoid "undeclared" warning about automatically generated pickle methods.
+ Patch by David Woods. (Github issue :issue:`3353`)
+
+* Avoid C compiler warning about unreachable code in ``prange()``.
+
+* Some C compiler warnings in PyPy were resolved.
+ Patch by Matti Picus. (Github issue :issue:`3437`)
+
+
+.. _0.29.15:
+
+
+0.29.15 (2020-02-06)
+====================
+
+Bugs fixed
+----------
+
+* Crash when returning a temporary Python object from an async-def function.
+ (Github issue :issue:`3337`)
+
+* Crash when using ``**kwargs`` in generators.
+ Patch by David Woods. (Github issue :issue:`3265`)
+
+* Double reference free in ``__class__`` cell handling for ``super()`` calls.
+ (Github issue :issue:`3246`)
+
+* Compile error when using ``*args`` as Python class bases.
+ (Github issue :issue:`3338`)
+
+* Import failure in IPython 7.11.
+ (Github issue :issue:`3297`)
+
+* Fixed C name collision in the auto-pickle code.
+ Patch by ThePrez. (Github issue :issue:`3238`)
+
+* Deprecated import failed in Python 3.9.
+ (Github issue :issue:`3266`)
+
+
+.. _0.29.14:
+
+0.29.14 (2019-11-01)
+====================
+
+Bugs fixed
+----------
+
+* The generated code failed to initialise the ``tp_print`` slot in CPython 3.8.
+ Patches by Pablo Galindo and Orivej Desh. (Github issues :issue:`3171`, :issue:`3201`)
+
+* ``?`` for ``bool`` was missing from the supported NumPy dtypes.
+ Patch by Max Klein. (Github issue :issue:`2675`)
+
+* ``await`` was not allowed inside of f-strings.
+ Patch by Dmitro Getz. (Github issue :issue:`2877`)
+
+* Coverage analysis failed for projects where the code resides in separate
+ source sub-directories.
+ Patch by Antonio Valentino. (Github issue :issue:`1985`)
+
+* An incorrect compiler warning was fixed in automatic C++ string conversions.
+ Patch by Gerion Entrup. (Github issue :issue:`3108`)
+
+* Error reports in the Jupyter notebook showed unhelpful stack traces.
+ Patch by Matthew Edwards (Github issue :issue:`3196`).
+
+* ``Python.h`` is now also included explicitly from ``public`` header files.
+ (Github issue :issue:`3133`).
+
+* Distutils builds with ``--parallel`` did not work when using Cython's
+ deprecated ``build_ext`` command.
+ Patch by Alphadelta14 (Github issue :issue:`3187`).
+
+Other changes
+-------------
+
+* The ``PyMemoryView_*()`` C-API is available in ``cpython.memoryview``.
+ Patch by Nathan Manville. (Github issue :issue:`2541`)
+
+
+0.29.13 (2019-07-26)
+====================
+
+Bugs fixed
+----------
+
+* A reference leak for ``None`` was fixed when converting a memoryview
+ to a Python object. (Github issue :issue:`3023`)
+
+* The declaration of ``PyGILState_STATE`` in ``cpython.pystate`` was unusable.
+ Patch by Kirill Smelkov. (Github issue :issue:`2997`)
+
+Other changes
+-------------
+
+* The declarations in ``posix.mman`` were extended.
+ Patches by Kirill Smelkov. (Github issues :issue:`2893`, :issue:`2894`, :issue:`3012`)
+
+
+0.29.12 (2019-07-07)
+====================
+
+Bugs fixed
+----------
+
+* Fix compile error in CPython 3.8b2 regarding the ``PyCode_New()`` signature.
+ (Github issue :issue:`3031`)
+
+* Fix a C compiler warning about a missing ``int`` downcast.
+ (Github issue :issue:`3028`)
+
+* Fix reported error positions of undefined builtins and constants.
+ Patch by Orivej Desh. (Github issue :issue:`3030`)
+
+* A 32 bit issue in the Pythran support was resolved.
+ Patch by Serge Guelton. (Github issue :issue:`3032`)
+
+
+0.29.11 (2019-06-30)
+====================
+
+Bugs fixed
+----------
+
+* Fix compile error in CPython 3.8b2 regarding the ``PyCode_New()`` signature.
+ Patch by Nick Coghlan. (Github issue :issue:`3009`)
+
+* Invalid C code generated for lambda functions in cdef methods.
+ Patch by Josh Tobin. (Github issue :issue:`2967`)
+
+* Support slice handling in newer Pythran versions.
+ Patch by Serge Guelton. (Github issue :issue:`2989`)
+
+* A reference leak in power-of-2 calculation was fixed.
+ Patch by Sebastian Berg. (Github issue :issue:`3022`)
+
+* The search order for include files was changed. Previously it was
+ ``include_directories``, ``Cython/Includes``, ``sys.path``. Now it is
+ ``include_directories``, ``sys.path``, ``Cython/Includes``. This was done to
+ allow third-party ``*.pxd`` files to override the ones in Cython.
+ Original patch by Matti Picus. (Github issue :issue:`2905`)
+
+* Setting ``language_level=2`` in a file did not work if ``language_level=3``
+ was enabled globally before.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2791`)
+
+
+0.29.10 (2019-06-02)
+====================
+
+Bugs fixed
+----------
+
+* Fix compile errors in CPython 3.8b1 due to the new "tp_vectorcall" slots.
+ (Github issue :issue:`2976`)
+
+
+0.29.9 (2019-05-29)
+===================
+
+Bugs fixed
+----------
+
+* Fix a crash regression in 0.29.8 when creating code objects fails.
+
+* Remove an incorrect cast when using true-division in C++ operations.
+ (Github issue :issue:`1950`)
+
+
+0.29.8 (2019-05-28)
+===================
+
+Bugs fixed
+----------
+
+* C compile errors with CPython 3.8 were resolved.
+ Patch by Marcel Plch. (Github issue :issue:`2938`)
+
+* Python tuple constants that compare equal but have different item
+ types could incorrectly be merged into a single constant.
+ (Github issue :issue:`2919`)
+
+* Non-ASCII characters in unprefixed strings could crash the compiler when
+ used with language level ``3str``.
+
+* Starred expressions in %-formatting tuples could fail to compile for
+ unicode strings. (Github issue :issue:`2939`)
+
+* Passing Python class references through ``cython.inline()`` was broken.
+ (Github issue :issue:`2936`)
+
+
+0.29.7 (2019-04-14)
+===================
+
+Bugs fixed
+----------
+
+* Crash when the shared Cython config module gets unloaded and another Cython
+ module reports an exceptions. Cython now makes sure it keeps an owned reference
+ to the module.
+ (Github issue :issue:`2885`)
+
+* Resolved a C89 compilation problem when enabling the fast-gil sharing feature.
+
+* Coverage reporting did not include the signature line of ``cdef`` functions.
+ (Github issue :issue:`1461`)
+
+* Casting a GIL-requiring function into a nogil function now issues a warning.
+ (Github issue :issue:`2879`)
+
+* Generators and coroutines were missing their return type annotation.
+ (Github issue :issue:`2884`)
+
+
+0.29.6 (2019-02-27)
+===================
+
+Bugs fixed
+----------
+
+* Fix a crash when accessing the ``__kwdefaults__`` special attribute of
+ fused functions. (Github issue :issue:`1470`)
+
+* Fix the parsing of buffer format strings that contain numeric sizes, which
+ could lead to incorrect input rejections. (Github issue :issue:`2845`)
+
+* Avoid a C #pragma in old gcc versions that was only added in GCC 4.6.
+ Patch by Michael Anselmi. (Github issue :issue:`2838`)
+
+* Auto-encoding of Unicode strings to UTF-8 C/C++ strings failed in Python 3,
+ even though the default encoding there is UTF-8.
+ (Github issue :issue:`2819`)
+
+
+0.29.5 (2019-02-09)
+===================
+
+Bugs fixed
+----------
+
+* Crash when defining a Python subclass of an extension type and repeatedly calling
+ a cpdef method on it. (Github issue :issue:`2823`)
+
+* Compiler crash when ``prange()`` loops appear inside of with-statements.
+ (Github issue :issue:`2780`)
+
+* Some C compiler warnings were resolved.
+ Patches by Christoph Gohlke. (Github issues :issue:`2815`, :issue:`2816`, :issue:`2817`, :issue:`2822`)
+
+* Python conversion of C++ enums failed in 0.29.
+ Patch by Orivej Desh. (Github issue :issue:`2767`)
+
+
+0.29.4 (2019-02-01)
+===================
+
+Bugs fixed
+----------
+
+* Division of numeric constants by a runtime value of 0 could fail to raise a
+ ``ZeroDivisionError``. (Github issue :issue:`2820`)
+
+
+0.29.3 (2019-01-19)
+===================
+
+Bugs fixed
+----------
+
+* Some C code for memoryviews was generated in a non-deterministic order.
+ Patch by Martijn van Steenbergen. (Github issue :issue:`2779`)
+
+* C89 compatibility was accidentally lost since 0.28.
+ Patches by gastineau and true-pasky. (Github issues :issue:`2778`, :issue:`2801`)
+
+* A C compiler cast warning was resolved.
+ Patch by Michael Buesch. (Github issue :issue:`2774`)
+
+* An compilation failure with complex numbers under MSVC++ was resolved.
+ (Github issue :issue:`2797`)
+
+* Coverage reporting could fail when modules were moved around after the build.
+ Patch by Wenjun Si. (Github issue :issue:`2776`)
+
+
+0.29.2 (2018-12-14)
+===================
+
+Bugs fixed
+----------
+
+* The code generated for deduplicated constants leaked some references.
+ (Github issue :issue:`2750`)
+
+* The declaration of ``sigismember()`` in ``libc.signal`` was corrected.
+ (Github issue :issue:`2756`)
+
+* Crashes in compiler and test runner were fixed.
+ (Github issue :issue:`2736`, :issue:`2755`)
+
+* A C compiler warning about an invalid safety check was resolved.
+ (Github issue :issue:`2731`)
+
+
+0.29.1 (2018-11-24)
+===================
+
+Bugs fixed
+----------
+
+* Extensions compiled with MinGW-64 under Windows could misinterpret integer
+ objects larger than 15 bit and return incorrect results.
+ (Github issue :issue:`2670`)
+
+* Cython no longer requires the source to be writable when copying its data
+ into a memory view slice.
+ Patch by Andrey Paramonov. (Github issue :issue:`2644`)
+
+* Line tracing of ``try``-statements generated invalid C code.
+ (Github issue :issue:`2274`)
+
+* When using the ``warn.undeclared`` directive, Cython's own code generated
+ warnings that are now fixed.
+ Patch by Nicolas Pauss. (Github issue :issue:`2685`)
+
+* Cython's memoryviews no longer require strides for setting the shape field
+ but only the ``PyBUF_ND`` flag to be set.
+ Patch by John Kirkham. (Github issue :issue:`2716`)
+
+* Some C compiler warnings about unused memoryview code were fixed.
+ Patch by Ho Cheuk Ting. (Github issue :issue:`2588`)
+
+* A C compiler warning about implicit signed/unsigned conversion was fixed.
+ (Github issue :issue:`2729`)
+
+* Assignments to C++ references returned by ``operator[]`` could fail to compile.
+ (Github issue :issue:`2671`)
+
+* The power operator and the support for NumPy math functions were fixed
+ in Pythran expressions.
+ Patch by Serge Guelton. (Github issues :issue:`2702`, :issue:`2709`)
+
+* Signatures with memory view arguments now show the expected type
+ when embedded in docstrings.
+ Patch by Matthew Chan and Benjamin Weigel. (Github issue :issue:`2634`)
+
+* Some ``from ... cimport ...`` constructs were not correctly considered
+ when searching modified dependencies in ``cythonize()`` to decide
+ whether to recompile a module.
+ Patch by Kryštof Pilnáček. (Github issue :issue:`2638`)
+
+* A struct field type in the ``cpython.array`` declarations was corrected.
+ Patch by John Kirkham. (Github issue :issue:`2712`)
+
+
+0.29 (2018-10-14)
+=================
+
+Features added
+--------------
+
+* PEP-489 multi-phase module initialisation has been enabled again. Module
+ reloads in other subinterpreters raise an exception to prevent corruption
+ of the static module state.
+
+* A set of ``mypy`` compatible PEP-484 declarations were added for Cython's C data
+ types to integrate with static analysers in typed Python code. They are available
+ in the ``Cython/Shadow.pyi`` module and describe the types in the special ``cython``
+ module that can be used for typing in Python code.
+ Original patch by Julian Gethmann. (Github issue :issue:`1965`)
+
+* Memoryviews are supported in PEP-484/526 style type declarations.
+ (Github issue :issue:`2529`)
+
+* ``@cython.nogil`` is supported as a C-function decorator in Python code.
+ (Github issue :issue:`2557`)
+
+* Raising exceptions from nogil code will automatically acquire the GIL, instead
+ of requiring an explicit ``with gil`` block.
+
+* C++ functions can now be declared as potentially raising both C++ and Python
+ exceptions, so that Cython can handle both correctly.
+ (Github issue :issue:`2615`)
+
+* ``cython.inline()`` supports a direct ``language_level`` keyword argument that
+ was previously only available via a directive.
+
+* A new language level name ``3str`` was added that mostly corresponds to language
+ level 3, but keeps unprefixed string literals as type 'str' in both Py2 and Py3,
+ and the builtin 'str' type unchanged. This will become the default in the next
+ Cython release and is meant to help user code a) transition more easily to this
+ new default and b) migrate to Python 3 source code semantics without making support
+ for Python 2.x difficult.
+
+* In CPython 3.6 and later, looking up globals in the module dict is almost
+ as fast as looking up C globals.
+ (Github issue :issue:`2313`)
+
+* For a Python subclass of an extension type, repeated method calls to non-overridden
+ cpdef methods can avoid the attribute lookup in Py3.6+, which makes them 4x faster.
+ (Github issue :issue:`2313`)
+
+* (In-)equality comparisons of objects to integer literals are faster.
+ (Github issue :issue:`2188`)
+
+* Some internal and 1-argument method calls are faster.
+
+* Modules that cimport many external extension types from other Cython modules
+ execute less import requests during module initialisation.
+
+* Constant tuples and slices are deduplicated and only created once per module.
+ (Github issue :issue:`2292`)
+
+* The coverage plugin considers more C file extensions such as ``.cc`` and ``.cxx``.
+ (Github issue :issue:`2266`)
+
+* The ``cythonize`` command accepts compile time variable values (as set by ``DEF``)
+ through the new ``-E`` option.
+ Patch by Jerome Kieffer. (Github issue :issue:`2315`)
+
+* ``pyximport`` can import from namespace packages.
+ Patch by Prakhar Goel. (Github issue :issue:`2294`)
+
+* Some missing numpy and CPython C-API declarations were added.
+ Patch by John Kirkham. (Github issues :issue:`2523`, :issue:`2520`, :issue:`2537`)
+
+* Declarations for the ``pylifecycle`` C-API functions were added in a new .pxd file
+ ``cpython.pylifecycle``.
+
+* The Pythran support was updated to work with the latest Pythran 0.8.7.
+ Original patch by Adrien Guinet. (Github issue :issue:`2600`)
+
+* ``%a`` is included in the string formatting types that are optimised into f-strings.
+ In this case, it is also automatically mapped to ``%r`` in Python 2.x.
+
+* New C macro ``CYTHON_HEX_VERSION`` to access Cython's version in the same style as
+ ``PY_VERSION_HEX``.
+
+* Constants in ``libc.math`` are now declared as ``const`` to simplify their handling.
+
+* An additional ``check_size`` clause was added to the ``ctypedef class`` name
+ specification to allow suppressing warnings when importing modules with
+ backwards-compatible ``PyTypeObject`` size changes.
+ Patch by Matti Picus. (Github issue :issue:`2627`)
+
+Bugs fixed
+----------
+
+* The exception handling in generators and coroutines under CPython 3.7 was adapted
+ to the newly introduced exception stack. Users of Cython 0.28 who want to support
+ Python 3.7 are encouraged to upgrade to 0.29 to avoid potentially incorrect error
+ reporting and tracebacks. (Github issue :issue:`1958`)
+
+* Crash when importing a module under Stackless Python that was built for CPython.
+ Patch by Anselm Kruis. (Github issue :issue:`2534`)
+
+* 2-value slicing of typed sequences failed if the start or stop index was None.
+ Patch by Christian Gibson. (Github issue :issue:`2508`)
+
+* Multiplied string literals lost their factor when they are part of another
+ constant expression (e.g. 'x' * 10 + 'y' => 'xy').
+
+* String formatting with the '%' operator didn't call the special ``__rmod__()``
+ method if the right side is a string subclass that implements it.
+ (Python issue 28598)
+
+* The directive ``language_level=3`` did not apply to the first token in the
+ source file. (Github issue :issue:`2230`)
+
+* Overriding cpdef methods did not work in Python subclasses with slots.
+ Note that this can have a performance impact on calls from Cython code.
+ (Github issue :issue:`1771`)
+
+* Fix declarations of builtin or C types using strings in pure python mode.
+ (Github issue :issue:`2046`)
+
+* Generator expressions and lambdas failed to compile in ``@cfunc`` functions.
+ (Github issue :issue:`459`)
+
+* Global names with ``const`` types were not excluded from star-import assignments
+ which could lead to invalid C code.
+ (Github issue :issue:`2621`)
+
+* Several internal function signatures were fixed that lead to warnings in gcc-8.
+ (Github issue :issue:`2363`)
+
+* The numpy helper functions ``set_array_base()`` and ``get_array_base()``
+ were adapted to the current numpy C-API recommendations.
+ Patch by Matti Picus. (Github issue :issue:`2528`)
+
+* Some NumPy related code was updated to avoid deprecated API usage.
+ Original patch by jbrockmendel. (Github issue :issue:`2559`)
+
+* Several C++ STL declarations were extended and corrected.
+ Patch by Valentin Valls. (Github issue :issue:`2207`)
+
+* C lines of the module init function were unconditionally not reported in
+ exception stack traces.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2492`)
+
+* When PEP-489 support is enabled, reloading the module overwrote any static
+ module state. It now raises an exception instead, given that reloading is
+ not actually supported.
+
+* Object-returning, C++ exception throwing functions were not checking that
+ the return value was non-null.
+ Original patch by Matt Wozniski (Github issue :issue:`2603`)
+
+* The source file encoding detection could get confused if the
+ ``c_string_encoding`` directive appeared within the first two lines.
+ (Github issue :issue:`2632`)
+
+* Cython generated modules no longer emit a warning during import when the
+ size of the NumPy array type is larger than what was found at compile time.
+ Instead, this is assumed to be a backwards compatible change on NumPy side.
+
+Other changes
+-------------
+
+* Cython now emits a warning when no ``language_level`` (2, 3 or '3str') is set
+ explicitly, neither as a ``cythonize()`` option nor as a compiler directive.
+ This is meant to prepare the transition of the default language level from
+ currently Py2 to Py3, since that is what most new users will expect these days.
+ The future default will, however, not enforce unicode literals, because this
+ has proven a major obstacle in the support for both Python 2.x and 3.x. The
+ next major release is intended to make this change, so that it will parse all
+ code that does not request a specific language level as Python 3 code, but with
+ ``str`` literals. The language level 2 will continue to be supported for an
+ indefinite time.
+
+* The documentation was restructured, cleaned up and examples are now tested.
+ The NumPy tutorial was also rewritten to simplify the running example.
+ Contributed by Gabriel de Marmiesse. (Github issue :issue:`2245`)
+
+* Cython compiles less of its own modules at build time to reduce the installed
+ package size to about half of its previous size. This makes the compiler
+ slightly slower, by about 5-7%.
+
+
+0.28.6 (2018-11-01)
+===================
+
+Bugs fixed
+----------
+
+* Extensions compiled with MinGW-64 under Windows could misinterpret integer
+ objects larger than 15 bit and return incorrect results.
+ (Github issue :issue:`2670`)
+
+* Multiplied string literals lost their factor when they are part of another
+ constant expression (e.g. 'x' * 10 + 'y' => 'xy').
+
+
+0.28.5 (2018-08-03)
+===================
+
+Bugs fixed
+----------
+
+* The discouraged usage of GCC's attribute ``optimize("Os")`` was replaced by the
+ similar attribute ``cold`` to reduce the code impact of the module init functions.
+ (Github issue :issue:`2494`)
+
+* A reference leak in Py2.x was fixed when comparing str to unicode for equality.
+
+
+0.28.4 (2018-07-08)
+===================
+
+Bugs fixed
+----------
+
+* Reallowing ``tp_clear()`` in a subtype of an ``@no_gc_clear`` extension type
+ generated an invalid C function call to the (non-existent) base type implementation.
+ (Github issue :issue:`2309`)
+
+* Exception catching based on a non-literal (runtime) tuple could fail to match the
+ exception. (Github issue :issue:`2425`)
+
+* Compile fix for CPython 3.7.0a2. (Github issue :issue:`2477`)
+
+
+0.28.3 (2018-05-27)
+===================
+
+Bugs fixed
+----------
+
+* Set iteration was broken in non-CPython since 0.28.
+
+* ``UnicodeEncodeError`` in Py2 when ``%s`` formatting is optimised for
+ unicode strings. (Github issue :issue:`2276`)
+
+* Work around a crash bug in g++ 4.4.x by disabling the size reduction setting
+ of the module init function in this version. (Github issue :issue:`2235`)
+
+* Crash when exceptions occur early during module initialisation.
+ (Github issue :issue:`2199`)
+
+
+0.28.2 (2018-04-13)
+===================
+
+Features added
+--------------
+
+* ``abs()`` is faster for Python long objects.
+
+* The C++11 methods ``front()`` and ``end()`` were added to the declaration of
+ ``libcpp.string``. Patch by Alex Huszagh. (Github issue :issue:`2123`)
+
+* The C++11 methods ``reserve()`` and ``bucket_count()`` are declared for
+ ``libcpp.unordered_map``. Patch by Valentin Valls. (Github issue :issue:`2168`)
+
+Bugs fixed
+----------
+
+* The copy of a read-only memoryview was considered read-only as well, whereas
+ a common reason to copy a read-only view is to make it writable. The result
+ of the copying is now a writable buffer by default.
+ (Github issue :issue:`2134`)
+
+* The ``switch`` statement generation failed to apply recursively to the body of
+ converted if-statements.
+
+* ``NULL`` was sometimes rejected as exception return value when the returned
+ type is a fused pointer type.
+ Patch by Callie LeFave. (Github issue :issue:`2177`)
+
+* Fixed compatibility with PyPy 5.11.
+ Patch by Matti Picus. (Github issue :issue:`2165`)
+
+Other changes
+-------------
+
+* The NumPy tutorial was rewritten to use memoryviews instead of the older
+ buffer declaration syntax.
+ Contributed by Gabriel de Marmiesse. (Github issue :issue:`2162`)
+
+
+0.28.1 (2018-03-18)
+===================
+
+Bugs fixed
+----------
+
+* ``PyFrozenSet_New()`` was accidentally used in PyPy where it is missing
+ from the C-API.
+
+* Assignment between some C++ templated types were incorrectly rejected
+ when the templates mix ``const`` with ``ctypedef``.
+ (Github issue :issue:`2148`)
+
+* Undeclared C++ no-args constructors in subclasses could make the compilation
+ fail if the base class constructor was declared without ``nogil``.
+ (Github issue :issue:`2157`)
+
+* Bytes %-formatting inferred ``basestring`` (bytes or unicode) as result type
+ in some cases where ``bytes`` would have been safe to infer.
+ (Github issue :issue:`2153`)
+
+* ``None`` was accidentally disallowed as typed return value of ``dict.pop()``.
+ (Github issue :issue:`2152`)
+
+
+0.28 (2018-03-13)
+=================
+
+Features added
+--------------
+
+* Cdef classes can now multiply inherit from ordinary Python classes.
+ (The primary base must still be a c class, possibly ``object``, and
+ the other bases must *not* be cdef classes.)
+
+* Type inference is now supported for Pythran compiled NumPy expressions.
+ Patch by Nils Braun. (Github issue :issue:`1954`)
+
+* The ``const`` modifier can be applied to memoryview declarations to allow
+ read-only buffers as input. (Github issues :issue:`1605`, :issue:`1869`)
+
+* C code in the docstring of a ``cdef extern`` block is copied verbatimly
+ into the generated file.
+ Patch by Jeroen Demeyer. (Github issue :issue:`1915`)
+
+* When compiling with gcc, the module init function is now tuned for small
+ code size instead of whatever compile flags were provided externally.
+ Cython now also disables some code intensive optimisations in that function
+ to further reduce the code size. (Github issue :issue:`2102`)
+
+* Decorating an async coroutine with ``@cython.iterable_coroutine`` changes its
+ type at compile time to make it iterable. While this is not strictly in line
+ with PEP-492, it improves the interoperability with old-style coroutines that
+ use ``yield from`` instead of ``await``.
+
+* The IPython magic has preliminary support for JupyterLab.
+ (Github issue :issue:`1775`)
+
+* The new TSS C-API in CPython 3.7 is supported and has been backported.
+ Patch by Naotoshi Seo. (Github issue :issue:`1932`)
+
+* Cython knows the new ``Py_tss_t`` type defined in PEP-539 and automatically
+ initialises variables declared with that type to ``Py_tss_NEEDS_INIT``,
+ a value which cannot be used outside of static assignments.
+
+* The set methods ``.remove()`` and ``.discard()`` are optimised.
+ Patch by Antoine Pitrou. (Github issue :issue:`2042`)
+
+* ``dict.pop()`` is optimised.
+ Original patch by Antoine Pitrou. (Github issue :issue:`2047`)
+
+* Iteration over sets and frozensets is optimised.
+ (Github issue :issue:`2048`)
+
+* Safe integer loops (< range(2^30)) are automatically optimised into C loops.
+
+* ``alist.extend([a,b,c])`` is optimised into sequential ``list.append()`` calls
+ for short literal sequences.
+
+* Calls to builtin methods that are not specifically optimised into C-API calls
+ now use a cache that avoids repeated lookups of the underlying C function.
+ (Github issue :issue:`2054`)
+
+* Single argument function calls can avoid the argument tuple creation in some cases.
+
+* Some redundant extension type checks are avoided.
+
+* Formatting C enum values in f-strings is faster, as well as some other special cases.
+
+* String formatting with the '%' operator is optimised into f-strings in simple cases.
+
+* Subscripting (item access) is faster in some cases.
+
+* Some ``bytearray`` operations have been optimised similar to ``bytes``.
+
+* Some PEP-484/526 container type declarations are now considered for
+ loop optimisations.
+
+* Indexing into memoryview slices with ``view[i][j]`` is now optimised into
+ ``view[i, j]``.
+
+* Python compatible ``cython.*`` types can now be mixed with type declarations
+ in Cython syntax.
+
+* Name lookups in the module and in classes are faster.
+
+* Python attribute lookups on extension types without instance dict are faster.
+
+* Some missing signals were added to ``libc/signal.pxd``.
+ Patch by Jeroen Demeyer. (Github issue :issue:`1914`)
+
+* The warning about repeated extern declarations is now visible by default.
+ (Github issue :issue:`1874`)
+
+* The exception handling of the function types used by CPython's type slot
+ functions was corrected to match the de-facto standard behaviour, so that
+ code that uses them directly benefits from automatic and correct exception
+ propagation. Patch by Jeroen Demeyer. (Github issue :issue:`1980`)
+
+* Defining the macro ``CYTHON_NO_PYINIT_EXPORT`` will prevent the module init
+ function from being exported as symbol, e.g. when linking modules statically
+ in an embedding setup. Patch by AraHaan. (Github issue :issue:`1944`)
+
+Bugs fixed
+----------
+
+* If a module name is explicitly provided for an ``Extension()`` that is compiled
+ via ``cythonize()``, it was previously ignored and replaced by the source file
+ name. It can now be used to override the target module name, e.g. for compiling
+ prefixed accelerator modules from Python files. (Github issue :issue:`2038`)
+
+* The arguments of the ``num_threads`` parameter of parallel sections
+ were not sufficiently validated and could lead to invalid C code.
+ (Github issue :issue:`1957`)
+
+* Catching exceptions with a non-trivial exception pattern could call into
+ CPython with a live exception set. This triggered incorrect behaviour
+ and crashes, especially in CPython 3.7.
+
+* The signature of the special ``__richcmp__()`` method was corrected to recognise
+ the type of the first argument as ``self``. It was previously treated as plain
+ object, but CPython actually guarantees that it always has the correct type.
+ Note: this can change the semantics of user code that previously relied on
+ ``self`` being untyped.
+
+* Some Python 3 exceptions were not recognised as builtins when running Cython
+ under Python 2.
+
+* Some async helper functions were not defined in the generated C code when
+ compiling simple async code. (Github issue :issue:`2075`)
+
+* Line tracing did not include generators and coroutines.
+ (Github issue :issue:`1949`)
+
+* C++ declarations for ``unordered_map`` were corrected.
+ Patch by Michael Schatzow. (Github issue :issue:`1484`)
+
+* Iterator declarations in C++ ``deque`` and ``vector`` were corrected.
+ Patch by Alex Huszagh. (Github issue :issue:`1870`)
+
+* The const modifiers in the C++ ``string`` declarations were corrected, together
+ with the coercion behaviour of string literals into C++ strings.
+ (Github issue :issue:`2132`)
+
+* Some declaration types in ``libc.limits`` were corrected.
+ Patch by Jeroen Demeyer. (Github issue :issue:`2016`)
+
+* ``@cython.final`` was not accepted on Python classes with an ``@cython.cclass``
+ decorator. (Github issue :issue:`2040`)
+
+* Cython no longer creates useless and incorrect ``PyInstanceMethod`` wrappers for
+ methods in Python 3. Patch by Jeroen Demeyer. (Github issue :issue:`2105`)
+
+* The builtin ``bytearray`` type could not be used as base type of cdef classes.
+ (Github issue :issue:`2106`)
+
+Other changes
+-------------
+
+
+0.27.3 (2017-11-03)
+===================
+
+Bugs fixed
+----------
+
+* String forward references to extension types like ``@cython.locals(x="ExtType")``
+ failed to find the named type. (Github issue :issue:`1962`)
+
+* NumPy slicing generated incorrect results when compiled with Pythran.
+ Original patch by Serge Guelton (Github issue :issue:`1946`).
+
+* Fix "undefined reference" linker error for generators on Windows in Py3.3-3.5.
+ (Github issue :issue:`1968`)
+
+* Adapt to recent C-API change of ``PyThreadState`` in CPython 3.7.
+
+* Fix signature of ``PyWeakref_GetObject()`` API declaration.
+ Patch by Jeroen Demeyer (Github issue :issue:`1975`).
+
+
+0.27.2 (2017-10-22)
+===================
+
+Bugs fixed
+----------
+
+* Comprehensions could incorrectly be optimised away when they appeared in boolean
+ test contexts. (Github issue :issue:`1920`)
+
+* The special methods ``__eq__``, ``__lt__`` etc. in extension types did not type
+ their first argument as the type of the class but ``object``. (Github issue :issue:`1935`)
+
+* Crash on first lookup of "cline_in_traceback" option during exception handling.
+ (Github issue :issue:`1907`)
+
+* Some nested module level comprehensions failed to compile.
+ (Github issue :issue:`1906`)
+
+* Compiler crash on some complex type declarations in pure mode.
+ (Github issue :issue:`1908`)
+
+* ``std::unordered_map.erase()`` was declared with an incorrect ``void`` return
+ type in ``libcpp.unordered_map``. (Github issue :issue:`1484`)
+
+* Invalid use of C++ ``fallthrough`` attribute before C++11 and similar issue in clang.
+ (Github issue :issue:`1930`)
+
+* Compiler crash on misnamed properties. (Github issue :issue:`1905`)
+
+
+0.27.1 (2017-10-01)
+===================
+
+Features added
+--------------
+
+* The Jupyter magic has a new debug option ``--verbose`` that shows details about
+ the distutils invocation. Patch by Boris Filippov (Github issue :issue:`1881`).
+
+Bugs fixed
+----------
+
+* Py3 list comprehensions in class bodies resulted in invalid C code.
+ (Github issue :issue:`1889`)
+
+* Modules built for later CPython 3.5.x versions failed to import in 3.5.0/3.5.1.
+ (Github issue :issue:`1880`)
+
+* Deallocating fused types functions and methods kept their GC tracking enabled,
+ which could potentially lead to recursive deallocation attempts.
+
+* Crash when compiling in C++ mode with old setuptools versions.
+ (Github issue :issue:`1879`)
+
+* C++ object arguments for the constructor of Cython implemented C++ are now
+ passed by reference and not by value to allow for non-copyable arguments, such
+ as ``unique_ptr``.
+
+* API-exported C++ classes with Python object members failed to compile.
+ (Github issue :issue:`1866`)
+
+* Some issues with the new relaxed exception value handling were resolved.
+
+* Python classes as annotation types could prevent compilation.
+ (Github issue :issue:`1887`)
+
+* Cython annotation types in Python files could lead to import failures
+ with a "cython undefined" error. Recognised types are now turned into strings.
+
+* Coverage analysis could fail to report on extension modules on some platforms.
+
+* Annotations could be parsed (and rejected) as types even with
+ ``annotation_typing=False``.
+
+Other changes
+-------------
+
+* PEP 489 support has been disabled by default to counter incompatibilities with
+ import setups that try to reload or reinitialise modules.
+
+
+0.27 (2017-09-23)
+=================
+
+Features added
+--------------
+
+* Extension module initialisation follows
+ `PEP 489 `_ in CPython 3.5+, which
+ resolves several differences with regard to normal Python modules. This makes
+ the global names ``__file__`` and ``__path__`` correctly available to module
+ level code and improves the support for module-level relative imports.
+ (Github issues :issue:`1715`, :issue:`1753`, :issue:`1035`)
+
+* Asynchronous generators (`PEP 525 `_)
+ and asynchronous comprehensions (`PEP 530 `_)
+ have been implemented. Note that async generators require finalisation support
+ in order to allow for asynchronous operations during cleanup, which is only
+ available in CPython 3.6+. All other functionality has been backported as usual.
+
+* Variable annotations are now parsed according to
+ `PEP 526 `_. Cython types (e.g.
+ ``cython.int``) are evaluated as C type declarations and everything else as Python
+ types. This can be disabled with the directive ``annotation_typing=False``.
+ Note that most complex PEP-484 style annotations are currently ignored. This will
+ change in future releases. (Github issue :issue:`1850`)
+
+* Extension types (also in pure Python mode) can implement the normal special methods
+ ``__eq__``, ``__lt__`` etc. for comparisons instead of the low-level ``__richcmp__``
+ method. (Github issue :issue:`690`)
+
+* New decorator ``@cython.exceptval(x=None, check=False)`` that makes the signature
+ declarations ``except x``, ``except? x`` and ``except *`` available to pure Python
+ code. Original patch by Antonio Cuni. (Github issue :issue:`1653`)
+
+* Signature annotations are now included in the signature docstring generated by
+ the ``embedsignature`` directive. Patch by Lisandro Dalcin (Github issue :issue:`1781`).
+
+* The gdb support for Python code (``libpython.py``) was updated to the latest
+ version in CPython 3.7 (git rev 5fe59f8).
+
+* The compiler tries to find a usable exception return value for cdef functions
+ with ``except *`` if the returned type allows it. Note that this feature is subject
+ to safety limitations, so it is still better to provide an explicit declaration.
+
+* C functions can be assigned to function pointers with a compatible exception
+ declaration, not only with exact matches. A side-effect is that certain compatible
+ signature overrides are now allowed and some more mismatches of exception signatures
+ are now detected and rejected as errors that were not detected before.
+
+* The IPython/Jupyter magic integration has a new option ``%%cython --pgo`` for profile
+ guided optimisation. It compiles the cell with PGO settings for the C compiler,
+ executes it to generate a runtime profile, and then compiles it again using that
+ profile for C compiler optimisation. Currently only tested with gcc.
+
+* ``len(memoryview)`` can be used in nogil sections to get the size of the
+ first dimension of a memory view (``shape[0]``). (Github issue :issue:`1733`)
+
+* C++ classes can now contain (properly refcounted) Python objects.
+
+* NumPy dtype subarrays are now accessible through the C-API.
+ Patch by Gerald Dalley (Github issue :issue:`245`).
+
+* Resolves several issues with PyPy and uses faster async slots in PyPy3.
+ Patch by Ronan Lamy (Github issues :issue:`1871`, :issue:`1878`).
+
+Bugs fixed
+----------
+
+* Extension types that were cimported from other Cython modules could disagree
+ about the order of fused cdef methods in their call table. This could lead
+ to wrong methods being called and potentially also crashes. The fix required
+ changes to the ordering of fused methods in the call table, which may break
+ existing compiled modules that call fused cdef methods across module boundaries,
+ if these methods were implemented in a different order than they were declared
+ in the corresponding .pxd file. (Github issue :issue:`1873`)
+
+* The exception state handling in generators and coroutines could lead to
+ exceptions in the caller being lost if an exception was raised and handled
+ inside of the coroutine when yielding. (Github issue :issue:`1731`)
+
+* Loops over ``range(enum)`` were not converted into C for-loops. Note that it
+ is still recommended to use an explicit cast to a C integer type in this case.
+
+* Error positions of names (e.g. variables) were incorrectly reported after the
+ name and not at the beginning of the name.
+
+* Compile time ``DEF`` assignments were evaluated even when they occur inside of
+ falsy ``IF`` blocks. (Github issue :issue:`1796`)
+
+* Disabling the line tracing from a trace function could fail.
+ Original patch by Dmitry Trofimov. (Github issue :issue:`1769`)
+
+* Several issues with the Pythran integration were resolved.
+
+* abs(signed int) now returns a signed rather than unsigned int.
+ (Github issue :issue:`1837`)
+
+* Reading ``frame.f_locals`` of a Cython function (e.g. from a debugger or profiler
+ could modify the module globals. (Github issue :issue:`1836`)
+
+* Buffer type mismatches in the NumPy buffer support could leak a reference to the
+ buffer owner.
+
+* Using the "is_f_contig" and "is_c_contig" memoryview methods together could leave
+ one of them undeclared. (Github issue :issue:`1872`)
+
+* Compilation failed if the for-in-range loop target was not a variable but a more
+ complex expression, e.g. an item assignment. (Github issue :issue:`1831`)
+
+* Compile time evaluations of (partially) constant f-strings could show incorrect
+ results.
+
+* Escape sequences in raw f-strings (``fr'...'``) were resolved instead of passing
+ them through as expected.
+
+* Some ref-counting issues in buffer error handling have been resolved.
+
+Other changes
+-------------
+
+* Type declarations in signature annotations are now parsed according to
+ `PEP 484 `_
+ typing. Only Cython types (e.g. ``cython.int``) and Python builtin types are
+ currently considered as type declarations. Everything else is ignored, but this
+ will change in a future Cython release.
+ (Github issue :issue:`1672`)
+
+* The directive ``annotation_typing`` is now ``True`` by default, which enables
+ parsing type declarations from annotations.
+
+* This release no longer supports Python 3.2.
+
+
+0.26.1 (2017-08-29)
+===================
+
+Features added
+--------------
+
+Bugs fixed
+----------
+
+* ``cython.view.array`` was missing ``.__len__()``.
+
+* Extension types with a ``.pxd`` override for their ``__releasebuffer__`` slot
+ (e.g. as provided by Cython for the Python ``array.array`` type) could leak
+ a reference to the buffer owner on release, thus not freeing the memory.
+ (Github issue :issue:`1638`)
+
+* Auto-decoding failed in 0.26 for strings inside of C++ containers.
+ (Github issue :issue:`1790`)
+
+* Compile error when inheriting from C++ container types.
+ (Github issue :issue:`1788`)
+
+* Invalid C code in generators (declaration after code).
+ (Github issue :issue:`1801`)
+
+* Arithmetic operations on ``const`` integer variables could generate invalid code.
+ (Github issue :issue:`1798`)
+
+* Local variables with names of special Python methods failed to compile inside of
+ closures. (Github issue :issue:`1797`)
+
+* Problem with indirect Emacs buffers in cython-mode.
+ Patch by Martin Albrecht (Github issue :issue:`1743`).
+
+* Extension types named ``result`` or ``PickleError`` generated invalid unpickling code.
+ Patch by Jason Madden (Github issue :issue:`1786`).
+
+* Bazel integration failed to compile ``.py`` files.
+ Patch by Guro Bokum (Github issue :issue:`1784`).
+
+* Some include directories and dependencies were referenced with their absolute paths
+ in the generated files despite lying within the project directory.
+
+* Failure to compile in Py3.7 due to a modified signature of ``_PyCFunctionFast()``
+
+
+0.26 (2017-07-19)
+=================
+
+Features added
+--------------
+
+* Pythran can be used as a backend for evaluating NumPy array expressions.
+ Patch by Adrien Guinet (Github issue :issue:`1607`).
+
+* cdef classes now support pickling by default when possible.
+ This can be disabled with the ``auto_pickle`` directive.
+
+* Speed up comparisons of strings if their hash value is available.
+ Patch by Claudio Freire (Github issue :issue:`1571`).
+
+* Support pyximport from zip files.
+ Patch by Sergei Lebedev (Github issue :issue:`1485`).
+
+* IPython magic now respects the ``__all__`` variable and ignores
+ names with leading-underscore (like ``import *`` does).
+ Patch by Syrtis Major (Github issue :issue:`1625`).
+
+* ``abs()`` is optimised for C complex numbers.
+ Patch by David Woods (Github issue :issue:`1648`).
+
+* The display of C lines in Cython tracebacks can now be enabled at runtime
+ via ``import cython_runtime; cython_runtime.cline_in_traceback=True``.
+ The default has been changed to False.
+
+* The overhead of calling fused types generic functions was reduced.
+
+* "cdef extern" include files are now also searched relative to the current file.
+ Patch by Jeroen Demeyer (Github issue :issue:`1654`).
+
+* Optional optimization for re-acquiring the GIL, controlled by the
+ `fast_gil` directive.
+
+Bugs fixed
+----------
+
+* Item lookup/assignment with a unicode character as index that is typed
+ (explicitly or implicitly) as ``Py_UCS4`` or ``Py_UNICODE`` used the
+ integer value instead of the Unicode string value. Code that relied on
+ the previous behaviour now triggers a warning that can be disabled by
+ applying an explicit cast. (Github issue :issue:`1602`)
+
+* f-string processing was adapted to changes in PEP 498 and CPython 3.6.
+
+* Invalid C code when decoding from UTF-16(LE/BE) byte strings.
+ (Github issue :issue:`1696`)
+
+* Unicode escapes in 'ur' raw-unicode strings were not resolved in Py2 code.
+ Original patch by Aaron Gallagher (Github issue :issue:`1594`).
+
+* File paths of code objects are now relative.
+ Original patch by Jelmer Vernooij (Github issue :issue:`1565`).
+
+* Decorators of cdef class methods could be executed twice.
+ Patch by Jeroen Demeyer (Github issue :issue:`1724`).
+
+* Dict iteration using the Py2 ``iter*`` methods failed in PyPy3.
+ Patch by Armin Rigo (Github issue :issue:`1631`).
+
+* Several warnings in the generated code are now suppressed.
+
+Other changes
+-------------
+
+* The ``unraisable_tracebacks`` option now defaults to ``True``.
+
+* Coercion of C++ containers to Python is no longer automatic on attribute
+ access (Github issue :issue:`1521`).
+
+* Access to Python attributes of cimported modules without the corresponding
+ import is now a compile-time (rather than runtime) error.
+
+* Do not use special dll linkage for "cdef public" functions.
+ Patch by Jeroen Demeyer (Github issue :issue:`1687`).
+
+* cdef/cpdef methods must match their declarations. See Github issue :issue:`1732`.
+ This is now a warning and will be an error in future releases.
+
+
+0.25.2 (2016-12-08)
+===================
+
+Bugs fixed
+----------
+
+* Fixes several issues with C++ template deduction.
+
+* Fixes a issue with bound method type inference (Github issue :issue:`551`).
+
+* Fixes a bug with cascaded tuple assignment (Github issue :issue:`1523`).
+
+* Fixed or silenced many Clang warnings.
+
+* Fixes bug with powers of pure real complex numbers (Github issue :issue:`1538`).
+
+
+0.25.1 (2016-10-26)
+===================
+
+Bugs fixed
+----------
+
+* Fixes a bug with ``isinstance(o, Exception)`` (Github issue :issue:`1496`).
+
+* Fixes bug with ``cython.view.array`` missing utility code in some cases
+ (Github issue :issue:`1502`).
+
+Other changes
+-------------
+
+* The distutils extension ``Cython.Distutils.build_ext`` has been reverted,
+ temporarily, to be ``old_build_ext`` to give projects time to migrate.
+ The new build_ext is available as ``new_build_ext``.
+
+
+0.25 (2016-10-25)
=================
Features added
@@ -19,7 +2637,8 @@
* The new METH_FASTCALL calling convention for PyCFunctions is supported
in CPython 3.6. See https://bugs.python.org/issue27810
-* Initial support for using Cython modules in Pyston. Patch by Daetalus.
+* Initial support for using Cython modules in Pyston.
+ Patch by Boxiang Sun.
* Dynamic Python attributes are allowed on cdef classes if an attribute
``cdef dict __dict__`` is declared in the class. Patch by empyrical.
@@ -34,7 +2653,7 @@
Patch by Claudio Freire.
* Buffer variables are no longer excluded from ``locals()``.
- Patch by da-woods.
+ Patch by David Woods.
* Building f-strings is faster, especially when formatting C integers.
@@ -53,8 +2672,8 @@
* Support for bazel using a the pyx_library rule in //Tools:rules.bzl.
-Bugs fixed
-----------
+Significant Bugs fixed
+----------------------
* Division of complex numbers avoids overflow by using Smith's method.
@@ -72,6 +2691,9 @@
to use cythonize which properly handles dependencies. The old extension can
still be found in ``Cython.Distutils.old_build_ext`` and is now deprecated.
+* ``directive_defaults`` is no longer available in ``Cython.Compiler.Options``,
+ use ``get_directive_defaults()`` instead.
+
0.24.1 (2016-07-15)
===================
@@ -82,7 +2704,7 @@
* IPython cell magic was lacking a good way to enable Python 3 code semantics.
It can now be used as "%%cython -3".
-* Follow a recent change in `PEP 492 `_
+* Follow a recent change in `PEP 492 `_
and CPython 3.5.2 that now requires the ``__aiter__()`` method of asynchronous
iterators to be a simple ``def`` method instead of an ``async def`` method.
@@ -112,12 +2734,12 @@
Features added
--------------
-* PEP 498: Literal String Formatting (f-strings).
+* `PEP 498 `_:
+ Literal String Formatting (f-strings).
Original patch by Jelle Zijlstra.
- https://www.python.org/dev/peps/pep-0498/
-* PEP 515: Underscores as visual separators in number literals.
- https://www.python.org/dev/peps/pep-0515/
+* `PEP 515 `_:
+ Underscores as visual separators in number literals.
* Parser was adapted to some minor syntax changes in Py3.6, e.g.
https://bugs.python.org/issue9232
@@ -179,7 +2801,7 @@
* Compile errors and warnings in integer type conversion code. This fixes
ticket 877. Patches by Christian Neukirchen, Nikolaus Rath, Ian Henriksen.
-* Reference leak when "*args" argument was reassigned in closures.
+* Reference leak when ``*args`` argument was reassigned in closures.
* Truth-testing Unicode strings could waste time and memory in Py3.3+.
@@ -300,11 +2922,11 @@
Features added
--------------
-* PEP 492 (async/await) was implemented.
- See https://www.python.org/dev/peps/pep-0492/
+* `PEP 492 `_
+ (async/await) was implemented.
-* PEP 448 (Additional Unpacking Generalizations) was implemented.
- See https://www.python.org/dev/peps/pep-0448/
+* `PEP 448 `_
+ (Additional Unpacking Generalizations) was implemented.
* Support for coverage.py 4.0+ can be enabled by adding the plugin
"Cython.Coverage" to the ".coveragerc" config file.
@@ -495,9 +3117,9 @@
* Anonymous C tuple types can be declared as (ctype1, ctype2, ...).
-* PEP 479: turn accidental StopIteration exceptions that exit generators
+* `PEP 479 `_:
+ turn accidental StopIteration exceptions that exit generators
into a RuntimeError, activated with future import "generator_stop".
- See https://www.python.org/dev/peps/pep-0479/
* Looping over ``reversed(range())`` is optimised in the same way as
``range()``. Patch by Favian Contreras.
@@ -635,7 +3257,7 @@
* Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function
- (following CPython 3.5). See http://bugs.python.org/issue21205
+ (following CPython 3.5). See https://bugs.python.org/issue21205
* The ``inline`` function modifier is available as a decorator
``@cython.inline`` in pure mode.
@@ -681,7 +3303,7 @@
evaluation and generally improves the code flow in the generated C code.
* The Python expression "2 ** N" is optimised into bit shifting.
- See http://bugs.python.org/issue21420
+ See https://bugs.python.org/issue21420
* Cascaded assignments (a = b = ...) try to minimise the number of
type coercions.
@@ -1470,11 +4092,11 @@
* Extension type inheritance from builtin types, such as "cdef class MyUnicode(unicode)", now works without further external type redeclarations (which are also strongly discouraged now and continue to issue a warning).
-* GDB support. http://docs.cython.org/src/userguide/debugging.html
+* GDB support. https://docs.cython.org/src/userguide/debugging.html
-* A new build system with support for inline distutils directives, correct dependency tracking, and parallel compilation. http://wiki.cython.org/enhancements/distutils_preprocessing
+* A new build system with support for inline distutils directives, correct dependency tracking, and parallel compilation. https://github.com/cython/cython/wiki/enhancements-distutils_preprocessing
-* Support for dynamic compilation at runtime via the new cython.inline function and cython.compile decorator. http://wiki.cython.org/enhancements/inline
+* Support for dynamic compilation at runtime via the new cython.inline function and cython.compile decorator. https://github.com/cython/cython/wiki/enhancements-inline
* "nogil" blocks are supported when compiling pure Python code by writing "with cython.nogil".
@@ -1650,7 +4272,7 @@
cdef np.ndarray[np.complex128_t, ndim=2] arr = \
np.zeros(10, np.complex128)
-* Cython can now generate a main()-method for embedding of the Python interpreter into an executable (see #289) [Robert Bradshaw]
+* Cython can now generate a main()-method for embedding of the Python interpreter into an executable (see :issue:`289`) [Robert Bradshaw]
* @wraparound directive (another way to disable arr[idx] for negative idx) [Dag Sverre Seljebotn]
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/BuildExecutable.py cython-0.20.1+1~202203241016-9537/Cython/Build/BuildExecutable.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/BuildExecutable.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/BuildExecutable.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,10 +1,10 @@
"""
-Compile a Python script into an executable that embeds CPython and run it.
+Compile a Python script into an executable that embeds CPython.
Requires CPython to be built as a shared library ('libpythonX.Y').
Basic usage:
- python cythonrun somefile.py [ARGS]
+ python -m Cython.Build.BuildExecutable [ARGS] somefile.py
"""
from __future__ import absolute_import
@@ -28,7 +28,7 @@
# no shared library
PYLIB_DYN = ''
else:
- PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ
+ PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ
CC = get_config_var('CC', os.environ.get('CC', ''))
CFLAGS = get_config_var('CFLAGS') + ' ' + os.environ.get('CFLAGS', '')
@@ -38,12 +38,14 @@
SYSLIBS = get_config_var('SYSLIBS')
EXE_EXT = sysconfig.get_config_var('EXE')
+
def _debug(msg, *args):
if DEBUG:
if args:
msg = msg % args
sys.stderr.write(msg + '\n')
+
def dump_config():
_debug('INCDIR: %s', INCDIR)
_debug('LIBDIR1: %s', LIBDIR1)
@@ -58,6 +60,26 @@
_debug('SYSLIBS: %s', SYSLIBS)
_debug('EXE_EXT: %s', EXE_EXT)
+
+def _parse_args(args):
+ cy_args = []
+ last_arg = None
+ for i, arg in enumerate(args):
+ if arg.startswith('-'):
+ cy_args.append(arg)
+ elif last_arg in ('-X', '--directive'):
+ cy_args.append(arg)
+ else:
+ input_file = arg
+ args = args[i+1:]
+ break
+ last_arg = arg
+ else:
+ raise ValueError('no input file provided')
+
+ return input_file, cy_args, args
+
+
def runcmd(cmd, shell=True):
if shell:
cmd = ' '.join(cmd)
@@ -65,24 +87,23 @@
else:
_debug(' '.join(cmd))
- try:
- import subprocess
- except ImportError: # Python 2.3 ...
- returncode = os.system(cmd)
- else:
- returncode = subprocess.call(cmd, shell=shell)
-
+ import subprocess
+ returncode = subprocess.call(cmd, shell=shell)
+
if returncode:
sys.exit(returncode)
+
def clink(basename):
runcmd([LINKCC, '-o', basename + EXE_EXT, basename+'.o', '-L'+LIBDIR1, '-L'+LIBDIR2]
+ [PYLIB_DYN and ('-l'+PYLIB_DYN) or os.path.join(LIBDIR1, PYLIB)]
+ LIBS.split() + SYSLIBS.split() + LINKFORSHARED.split())
+
def ccompile(basename):
runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAGS.split())
+
def cycompile(input_file, options=()):
from ..Compiler import Version, CmdLine, Main
options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embed', input_file])
@@ -91,9 +112,11 @@
if result.num_errors > 0:
sys.exit(1)
+
def exec_file(program_name, args=()):
runcmd([os.path.abspath(program_name)] + list(args), shell=False)
+
def build(input_file, compiler_args=(), force=False):
"""
Build an executable program from a Cython module.
@@ -105,7 +128,7 @@
if not force and os.path.abspath(exe_file) == os.path.abspath(input_file):
raise ValueError("Input and output file names are the same, refusing to overwrite")
if (not force and os.path.exists(exe_file) and os.path.exists(input_file)
- and os.path.getmtime(input_file) <= os.path.getmtime(exe_file)):
+ and os.path.getmtime(input_file) <= os.path.getmtime(exe_file)):
_debug("File is up to date, not regenerating %s", exe_file)
return exe_file
cycompile(input_file, compiler_args)
@@ -113,30 +136,22 @@
clink(basename)
return exe_file
+
def build_and_run(args):
"""
- Build an executable program from a Cython module and runs it.
+ Build an executable program from a Cython module and run it.
- Arguments after the module name will be passed verbatimely to the
- program.
+ Arguments after the module name will be passed verbatimly to the program.
"""
- cy_args = []
- last_arg = None
- for i, arg in enumerate(args):
- if arg.startswith('-'):
- cy_args.append(arg)
- elif last_arg in ('-X', '--directive'):
- cy_args.append(arg)
- else:
- input_file = arg
- args = args[i+1:]
- break
- last_arg = arg
- else:
- raise ValueError('no input file provided')
+ program_name, args = _build(args)
+ exec_file(program_name, args)
+
+def _build(args):
+ input_file, cy_args, args = _parse_args(args)
program_name = build(input_file, cy_args)
- exec_file(program_name, args)
+ return program_name, args
+
if __name__ == '__main__':
- build_and_run(sys.argv[1:])
+ _build(sys.argv[1:])
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Cythonize.py cython-0.20.1+1~202203241016-9537/Cython/Build/Cythonize.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Cythonize.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Cythonize.py 2022-03-24 10:16:46.000000000 +0000
@@ -21,40 +21,26 @@
class _FakePool(object):
def map_async(self, func, args):
- from itertools import imap
+ try:
+ from itertools import imap
+ except ImportError:
+ imap=map
for _ in imap(func, args):
pass
- def close(self): pass
- def terminate(self): pass
- def join(self): pass
-
-
-def parse_directives(option, name, value, parser):
- dest = option.dest
- old_directives = dict(getattr(parser.values, dest,
- Options.get_directive_defaults()))
- directives = Options.parse_directive_list(
- value, relaxed_bool=True, current_settings=old_directives)
- setattr(parser.values, dest, directives)
-
-
-def parse_options(option, name, value, parser):
- dest = option.dest
- options = dict(getattr(parser.values, dest, {}))
- for opt in value.split(','):
- if '=' in opt:
- n, v = opt.split('=', 1)
- v = v.lower() not in ('false', 'f', '0', 'no')
- else:
- n, v = opt, True
- options[n] = v
- setattr(parser.values, dest, options)
+ def close(self):
+ pass
+
+ def terminate(self):
+ pass
+
+ def join(self):
+ pass
def find_package_base(path):
base_dir, package_path = os.path.split(path)
- while os.path.isfile(os.path.join(base_dir, '__init__.py')):
+ while is_package_dir(base_dir):
base_dir, parent = os.path.split(base_dir)
package_path = '%s/%s' % (parent, package_path)
return base_dir, package_path
@@ -85,8 +71,10 @@
exclude_failures=options.keep_going,
exclude=options.excludes,
compiler_directives=options.directives,
+ compile_time_env=options.compile_time_env,
force=options.force,
quiet=options.quiet,
+ depfile=options.depfile,
**options.options)
if ext_modules and options.build:
@@ -132,57 +120,90 @@
shutil.rmtree(temp_dir)
-def parse_args(args):
- from optparse import OptionParser
- parser = OptionParser(usage='%prog [options] [sources and packages]+')
-
- parser.add_option('-X', '--directive', metavar='NAME=VALUE,...', dest='directives',
- type=str, action='callback', callback=parse_directives, default={},
+def create_args_parser():
+ from argparse import ArgumentParser
+ from ..Compiler.CmdLine import ParseDirectivesAction, ParseOptionsAction, ParseCompileTimeEnvAction
+
+ parser = ArgumentParser()
+
+ parser.add_argument('-X', '--directive', metavar='NAME=VALUE,...',
+ dest='directives', default={}, type=str,
+ action=ParseDirectivesAction,
help='set a compiler directive')
- parser.add_option('-s', '--option', metavar='NAME=VALUE', dest='options',
- type=str, action='callback', callback=parse_options, default={},
+ parser.add_argument('-E', '--compile-time-env', metavar='NAME=VALUE,...',
+ dest='compile_time_env', default={}, type=str,
+ action=ParseCompileTimeEnvAction,
+ help='set a compile time environment variable')
+ parser.add_argument('-s', '--option', metavar='NAME=VALUE',
+ dest='options', default={}, type=str,
+ action=ParseOptionsAction,
help='set a cythonize option')
- parser.add_option('-3', dest='python3_mode', action='store_true',
+ parser.add_argument('-2', dest='language_level', action='store_const', const=2, default=None,
+ help='use Python 2 syntax mode by default')
+ parser.add_argument('-3', dest='language_level', action='store_const', const=3,
help='use Python 3 syntax mode by default')
- parser.add_option('-a', '--annotate', dest='annotate', action='store_true',
- help='generate annotated HTML page for source files')
-
- parser.add_option('-x', '--exclude', metavar='PATTERN', dest='excludes',
+ parser.add_argument('--3str', dest='language_level', action='store_const', const='3str',
+ help='use Python 3 syntax mode by default')
+ parser.add_argument('-a', '--annotate', action='store_const', const='default', dest='annotate',
+ help='Produce a colorized HTML version of the source.')
+ parser.add_argument('--annotate-fullc', action='store_const', const='fullc', dest='annotate',
+ help='Produce a colorized HTML version of the source '
+ 'which includes entire generated C/C++-code.')
+ parser.add_argument('-x', '--exclude', metavar='PATTERN', dest='excludes',
action='append', default=[],
help='exclude certain file patterns from the compilation')
- parser.add_option('-b', '--build', dest='build', action='store_true',
+ parser.add_argument('-b', '--build', dest='build', action='store_true', default=None,
help='build extension modules using distutils')
- parser.add_option('-i', '--inplace', dest='build_inplace', action='store_true',
+ parser.add_argument('-i', '--inplace', dest='build_inplace', action='store_true', default=None,
help='build extension modules in place using distutils (implies -b)')
- parser.add_option('-j', '--parallel', dest='parallel', metavar='N',
+ parser.add_argument('-j', '--parallel', dest='parallel', metavar='N',
type=int, default=parallel_compiles,
help=('run builds in N parallel jobs (default: %d)' %
parallel_compiles or 1))
- parser.add_option('-f', '--force', dest='force', action='store_true',
+ parser.add_argument('-f', '--force', dest='force', action='store_true', default=None,
help='force recompilation')
- parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
+ parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=None,
help='be less verbose during compilation')
- parser.add_option('--lenient', dest='lenient', action='store_true',
+ parser.add_argument('--lenient', dest='lenient', action='store_true', default=None,
help='increase Python compatibility by ignoring some compile time errors')
- parser.add_option('-k', '--keep-going', dest='keep_going', action='store_true',
+ parser.add_argument('-k', '--keep-going', dest='keep_going', action='store_true', default=None,
help='compile as much as possible, ignore compilation failures')
+ parser.add_argument('--no-docstrings', dest='no_docstrings', action='store_true', default=None,
+ help='strip docstrings')
+ parser.add_argument('-M', '--depfile', action='store_true', help='produce depfiles for the sources')
+ parser.add_argument('sources', nargs='*')
+ return parser
+
+
+def parse_args_raw(parser, args):
+ options, unknown = parser.parse_known_args(args)
+ sources = options.sources
+ # if positional arguments were interspersed
+ # some of them are in unknown
+ for option in unknown:
+ if option.startswith('-'):
+ parser.error("unknown option "+option)
+ else:
+ sources.append(option)
+ del options.sources
+ return (options, sources)
+
+
+def parse_args(args):
+ parser = create_args_parser()
+ options, args = parse_args_raw(parser, args)
- options, args = parser.parse_args(args)
if not args:
parser.error("no source files provided")
if options.build_inplace:
options.build = True
if multiprocessing is None:
options.parallel = 0
- if options.python3_mode:
- options.options['language_level'] = 3
- return options, args
-
-
-def main(args=None):
- options, paths = parse_args(args)
+ if options.language_level:
+ assert options.language_level in (2, 3, '3str')
+ options.options['language_level'] = options.language_level
if options.lenient:
# increase Python compatibility by ignoring compile time errors
@@ -190,7 +211,16 @@
Options.error_on_uninitialized = False
if options.annotate:
- Options.annotate = True
+ Options.annotate = options.annotate
+
+ if options.no_docstrings:
+ Options.docstrings = False
+
+ return options, args
+
+
+def main(args=None):
+ options, paths = parse_args(args)
for path in paths:
cython_compile(path, options)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Dependencies.py cython-0.20.1+1~202203241016-9537/Cython/Build/Dependencies.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Dependencies.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Dependencies.py 2022-03-24 10:16:46.000000000 +0000
@@ -4,8 +4,23 @@
from .. import __version__
import collections
-import re, os, sys, time
+import contextlib
+import hashlib
+import os
+import shutil
+import subprocess
+import re, sys, time
from glob import iglob
+from io import open as io_open
+from os.path import relpath as _relpath
+from distutils.extension import Extension
+from distutils.util import strtobool
+import zipfile
+
+try:
+ from collections.abc import Iterable
+except ImportError:
+ from collections import Iterable
try:
import gzip
@@ -14,41 +29,24 @@
except ImportError:
gzip_open = open
gzip_ext = ''
-import shutil
-import subprocess
-
-try:
- import hashlib
-except ImportError:
- import md5 as hashlib
try:
- from io import open as io_open
+ import zlib
+ zipfile_compression_mode = zipfile.ZIP_DEFLATED
except ImportError:
- from codecs import open as io_open
+ zipfile_compression_mode = zipfile.ZIP_STORED
try:
- from os.path import relpath as _relpath
-except ImportError:
- # Py<2.6
- def _relpath(path, start=os.path.curdir):
- if not path:
- raise ValueError("no path specified")
- start_list = os.path.abspath(start).split(os.path.sep)
- path_list = os.path.abspath(path).split(os.path.sep)
- i = len(os.path.commonprefix([start_list, path_list]))
- rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return os.path.curdir
- return os.path.join(*rel_list)
-
-
-from distutils.extension import Extension
+ import pythran
+except:
+ pythran = None
from .. import Utils
from ..Utils import (cached_function, cached_method, path_exists,
safe_makedirs, copy_file_to_dir_if_newer, is_package_dir)
-from ..Compiler.Main import Context, CompilationOptions, default_options
+from ..Compiler import Errors
+from ..Compiler.Main import Context
+from ..Compiler.Options import CompilationOptions, default_options
join_path = cached_function(os.path.join)
copy_once_if_newer = cached_function(copy_file_to_dir_if_newer)
@@ -69,6 +67,15 @@
basestring = str
+def _make_relative(file_paths, base=None):
+ if not base:
+ base = os.getcwd()
+ if base[-1] != os.path.sep:
+ base += os.path.sep
+ return [_relpath(path, base) if path.startswith(base) else path
+ for path in file_paths]
+
+
def extended_iglob(pattern):
if '{' in pattern:
m = re.match('(.*){([^}]+)}(.*)', pattern)
@@ -110,20 +117,42 @@
@cached_function
def file_hash(filename):
- path = os.path.normpath(filename.encode("UTF-8"))
- m = hashlib.md5(str(len(path)) + ":")
- m.update(path)
- f = open(filename, 'rb')
- try:
+ path = os.path.normpath(filename)
+ prefix = ('%d:%s' % (len(path), path)).encode("UTF-8")
+ m = hashlib.sha1(prefix)
+ with open(path, 'rb') as f:
data = f.read(65000)
while data:
m.update(data)
data = f.read(65000)
- finally:
- f.close()
return m.hexdigest()
+def update_pythran_extension(ext):
+ if pythran is None:
+ raise RuntimeError("You first need to install Pythran to use the np_pythran directive.")
+ try:
+ pythran_ext = pythran.config.make_extension(python=True)
+ except TypeError: # older pythran version only
+ pythran_ext = pythran.config.make_extension()
+
+ ext.include_dirs.extend(pythran_ext['include_dirs'])
+ ext.extra_compile_args.extend(pythran_ext['extra_compile_args'])
+ ext.extra_link_args.extend(pythran_ext['extra_link_args'])
+ ext.define_macros.extend(pythran_ext['define_macros'])
+ ext.undef_macros.extend(pythran_ext['undef_macros'])
+ ext.library_dirs.extend(pythran_ext['library_dirs'])
+ ext.libraries.extend(pythran_ext['libraries'])
+ ext.language = 'c++'
+
+ # These options are not compatible with the way normal Cython extensions work
+ for bad_option in ["-fwhole-program", "-fvisibility=hidden"]:
+ try:
+ ext.extra_compile_args.remove(bad_option)
+ except ValueError:
+ pass
+
+
def parse_list(s):
"""
>>> parse_list("")
@@ -156,6 +185,7 @@
transitive_str = object()
transitive_list = object()
+bool_or = object()
distutils_settings = {
'name': str,
@@ -172,6 +202,7 @@
'export_symbols': list,
'depends': transitive_list,
'language': transitive_str,
+ 'np_pythran': bool_or
}
@@ -203,19 +234,23 @@
if line[0] != '#':
break
line = line[1:].lstrip()
- if line[:10] == 'distutils:':
- key, _, value = [s.strip() for s in line[10:].partition('=')]
- type = distutils_settings[key]
+ kind = next((k for k in ("distutils:","cython:") if line.startswith(k)), None)
+ if kind is not None:
+ key, _, value = [s.strip() for s in line[len(kind):].partition('=')]
+ type = distutils_settings.get(key, None)
+ if line.startswith("cython:") and type is None: continue
if type in (list, transitive_list):
value = parse_list(value)
if key == 'define_macros':
value = [tuple(macro.split('=', 1))
if '=' in macro else (macro, None)
for macro in value]
+ if type is bool_or:
+ value = strtobool(value)
self.values[key] = value
elif exn is not None:
for key in distutils_settings:
- if key in ('name', 'sources'):
+ if key in ('name', 'sources','np_pythran'):
continue
value = getattr(exn, key, None)
if value:
@@ -237,6 +272,8 @@
all.append(v)
value = all
self.values[key] = value
+ elif type is bool_or:
+ self.values[key] = self.values.get(key, False) | value
return self
def subs(self, aliases):
@@ -286,7 +323,8 @@
in_quote = False
hash_mark = single_q = double_q = -1
code_len = len(code)
- quote_type = quote_len = None
+ quote_type = None
+ quote_len = -1
while True:
if hash_mark < q:
@@ -366,6 +404,10 @@
r"(?:^\s*cimport +([0-9a-zA-Z_.]+(?: *, *[0-9a-zA-Z_.]+)*))|"
r"(?:^\s*cdef +extern +from +['\"]([^'\"]+)['\"])|"
r"(?:^\s*include +['\"]([^'\"]+)['\"])", re.M)
+dependency_after_from_regex = re.compile(
+ r"(?:^\s+\(([0-9a-zA-Z_., ]*)\)[#\n])|"
+ r"(?:^\s+([0-9a-zA-Z_., ]*)[#\n])",
+ re.M)
def normalize_existing(base_path, rel_paths):
@@ -374,14 +416,30 @@
@cached_function
def normalize_existing0(base_dir, rel_paths):
+ """
+ Given some base directory ``base_dir`` and a list of path names
+ ``rel_paths``, normalize each relative path name ``rel`` by
+ replacing it by ``os.path.join(base, rel)`` if that file exists.
+
+ Return a couple ``(normalized, needed_base)`` where ``normalized``
+ if the list of normalized file names and ``needed_base`` is
+ ``base_dir`` if we actually needed ``base_dir``. If no paths were
+ changed (for example, if all paths were already absolute), then
+ ``needed_base`` is ``None``.
+ """
normalized = []
+ needed_base = None
for rel in rel_paths:
+ if os.path.isabs(rel):
+ normalized.append(rel)
+ continue
path = join_path(base_dir, rel)
if path_exists(path):
normalized.append(os.path.normpath(path))
+ needed_base = base_dir
else:
normalized.append(rel)
- return normalized
+ return (normalized, needed_base)
def resolve_depends(depends, include_dirs):
@@ -425,11 +483,8 @@
# Actual parsing is way too slow, so we use regular expressions.
# The only catch is that we must strip comments and string
# literals ahead of time.
- fh = Utils.open_source_file(source_filename, error_handling='ignore')
- try:
+ with Utils.open_source_file(source_filename, error_handling='ignore') as fh:
source = fh.read()
- finally:
- fh.close()
distutils_info = DistutilsInfo(source)
source, literals = strip_string_literals(source)
source = source.replace('\\\n', ' ').replace('\t', ' ')
@@ -442,6 +497,13 @@
cimport_from, cimport_list, extern, include = m.groups()
if cimport_from:
cimports.append(cimport_from)
+ m_after_from = dependency_after_from_regex.search(source, pos=m.end())
+ if m_after_from:
+ multiline, one_line = m_after_from.groups()
+ subimports = multiline or one_line
+ cimports.extend("{0}.{1}".format(cimport_from, s.strip())
+ for s in subimports.split(','))
+
elif cimport_list:
cimports.extend(x.strip() for x in cimport_list.split(","))
elif extern:
@@ -471,31 +533,36 @@
for include in self.parse_dependencies(filename)[1]:
include_path = join_path(os.path.dirname(filename), include)
if not path_exists(include_path):
- include_path = self.context.find_include_file(include, None)
+ include_path = self.context.find_include_file(include, source_file_path=filename)
if include_path:
if '.' + os.path.sep in include_path:
include_path = os.path.normpath(include_path)
all.add(include_path)
all.update(self.included_files(include_path))
elif not self.quiet:
- print("Unable to locate '%s' referenced from '%s'" % (filename, include))
+ print(u"Unable to locate '%s' referenced from '%s'" % (filename, include))
return all
@cached_method
- def cimports_and_externs(self, filename):
+ def cimports_externs_incdirs(self, filename):
# This is really ugly. Nested cimports are resolved with respect to the
# includer, but includes are resolved with respect to the includee.
cimports, includes, externs = self.parse_dependencies(filename)[:3]
cimports = set(cimports)
externs = set(externs)
+ incdirs = set()
for include in self.included_files(filename):
- included_cimports, included_externs = self.cimports_and_externs(include)
+ included_cimports, included_externs, included_incdirs = self.cimports_externs_incdirs(include)
cimports.update(included_cimports)
externs.update(included_externs)
- return tuple(cimports), normalize_existing(filename, externs)
+ incdirs.update(included_incdirs)
+ externs, incdir = normalize_existing(filename, externs)
+ if incdir:
+ incdirs.add(incdir)
+ return tuple(cimports), externs, incdirs
def cimports(self, filename):
- return self.cimports_and_externs(filename)[0]
+ return self.cimports_externs_incdirs(filename)[0]
def package(self, filename):
return package(filename)
@@ -520,35 +587,36 @@
return None # FIXME: error?
module_path.pop(0)
relative = '.'.join(package_path + module_path)
- pxd = self.context.find_pxd_file(relative, None)
+ pxd = self.context.find_pxd_file(relative, source_file_path=filename)
if pxd:
return pxd
if is_relative:
return None # FIXME: error?
- return self.context.find_pxd_file(module, None)
+ return self.context.find_pxd_file(module, source_file_path=filename)
@cached_method
def cimported_files(self, filename):
- if filename[-4:] == '.pyx' and path_exists(filename[:-4] + '.pxd'):
- pxd_list = [filename[:-4] + '.pxd']
+ filename_root, filename_ext = os.path.splitext(filename)
+ if filename_ext in ('.pyx', '.py') and path_exists(filename_root + '.pxd'):
+ pxd_list = [filename_root + '.pxd']
else:
pxd_list = []
+ # Cimports generates all possible combinations package.module
+ # when imported as from package cimport module.
for module in self.cimports(filename):
if module[:7] == 'cython.' or module == 'cython':
continue
pxd_file = self.find_pxd(module, filename)
if pxd_file is not None:
pxd_list.append(pxd_file)
- elif not self.quiet:
- print("%s: cannot find cimported module '%s'" % (filename, module))
return tuple(pxd_list)
@cached_method
def immediate_dependencies(self, filename):
- all = set([filename])
- all.update(self.cimported_files(filename))
- all.update(self.included_files(filename))
- return all
+ all_deps = {filename}
+ all_deps.update(self.cimported_files(filename))
+ all_deps.update(self.included_files(filename))
+ return all_deps
def all_dependencies(self, filename):
return self.transitive_merge(filename, self.immediate_dependencies, set.union)
@@ -563,27 +631,56 @@
def newest_dependency(self, filename):
return max([self.extract_timestamp(f) for f in self.all_dependencies(filename)])
- def transitive_fingerprint(self, filename, extra=None):
+ def transitive_fingerprint(self, filename, module, compilation_options):
+ r"""
+ Return a fingerprint of a cython file that is about to be cythonized.
+
+ Fingerprints are looked up in future compilations. If the fingerprint
+ is found, the cythonization can be skipped. The fingerprint must
+ incorporate everything that has an influence on the generated code.
+ """
try:
- m = hashlib.md5(__version__)
- m.update(file_hash(filename))
+ m = hashlib.sha1(__version__.encode('UTF-8'))
+ m.update(file_hash(filename).encode('UTF-8'))
for x in sorted(self.all_dependencies(filename)):
if os.path.splitext(x)[1] not in ('.c', '.cpp', '.h'):
- m.update(file_hash(x))
- if extra is not None:
- m.update(str(extra))
+ m.update(file_hash(x).encode('UTF-8'))
+ # Include the module attributes that change the compilation result
+ # in the fingerprint. We do not iterate over module.__dict__ and
+ # include almost everything here as users might extend Extension
+ # with arbitrary (random) attributes that would lead to cache
+ # misses.
+ m.update(str((
+ module.language,
+ getattr(module, 'py_limited_api', False),
+ getattr(module, 'np_pythran', False)
+ )).encode('UTF-8'))
+
+ m.update(compilation_options.get_fingerprint().encode('UTF-8'))
return m.hexdigest()
except IOError:
return None
def distutils_info0(self, filename):
info = self.parse_dependencies(filename)[3]
- externs = self.cimports_and_externs(filename)[1]
+ kwds = info.values
+ cimports, externs, incdirs = self.cimports_externs_incdirs(filename)
+ basedir = os.getcwd()
+ # Add dependencies on "cdef extern from ..." files
if externs:
- if 'depends' in info.values:
- info.values['depends'] = list(set(info.values['depends']).union(externs))
+ externs = _make_relative(externs, basedir)
+ if 'depends' in kwds:
+ kwds['depends'] = list(set(kwds['depends']).union(externs))
else:
- info.values['depends'] = list(externs)
+ kwds['depends'] = list(externs)
+ # Add include_dirs to ensure that the C compiler will find the
+ # "cdef extern from ..." files
+ if incdirs:
+ include_dirs = list(kwds.get('include_dirs', []))
+ for inc in _make_relative(incdirs, basedir):
+ if inc not in include_dirs:
+ include_dirs.append(inc)
+ kwds['include_dirs'] = include_dirs
return info
def distutils_info(self, filename, aliases=None, base=None):
@@ -636,18 +733,33 @@
return _dep_tree
+# If this changes, change also docs/src/reference/compilation.rst
+# which mentions this function
+def default_create_extension(template, kwds):
+ if 'depends' in kwds:
+ include_dirs = kwds.get('include_dirs', []) + ["."]
+ depends = resolve_depends(kwds['depends'], include_dirs)
+ kwds['depends'] = sorted(set(depends + template.depends))
+
+ t = template.__class__
+ ext = t(**kwds)
+ metadata = dict(distutils=kwds, module_name=kwds['name'])
+ return (ext, metadata)
+
+
# This may be useful for advanced users?
def create_extension_list(patterns, exclude=None, ctx=None, aliases=None, quiet=False, language=None,
exclude_failures=False):
if language is not None:
- print('Please put "# distutils: language=%s" in your .pyx or .pxd file(s)' % language)
+ print('Warning: passing language={0!r} to cythonize() is deprecated. '
+ 'Instead, put "# distutils: language={0}" in your .pyx or .pxd file(s)'.format(language))
if exclude is None:
exclude = []
if patterns is None:
return [], {}
- elif isinstance(patterns, basestring) or not isinstance(patterns, collections.Iterable):
+ elif isinstance(patterns, basestring) or not isinstance(patterns, Iterable):
patterns = [patterns]
- explicit_modules = set([m.name for m in patterns if isinstance(m, Extension)])
+ explicit_modules = {m.name for m in patterns if isinstance(m, Extension)}
seen = set()
deps = create_dependency_tree(ctx, quiet=quiet)
to_exclude = set()
@@ -668,18 +780,28 @@
Extension_distutils = Extension
class Extension_setuptools(Extension): pass
+ # if no create_extension() function is defined, use a simple
+ # default function.
+ create_extension = ctx.options.create_extension or default_create_extension
+
for pattern in patterns:
+ if not isinstance(pattern, (Extension_distutils, Extension_setuptools)):
+ pattern = encode_filename_in_py2(pattern)
if isinstance(pattern, str):
filepattern = pattern
- template = None
+ template = Extension(pattern, []) # Fake Extension without sources
name = '*'
base = None
- exn_type = Extension
ext_language = language
elif isinstance(pattern, (Extension_distutils, Extension_setuptools)):
- for filepattern in pattern.sources:
- if os.path.splitext(filepattern)[1] in ('.py', '.pyx'):
- break
+ cython_sources = [s for s in pattern.sources
+ if os.path.splitext(s)[1] in ('.py', '.pyx')]
+ if cython_sources:
+ filepattern = cython_sources[0]
+ if len(cython_sources) > 1:
+ print(u"Warning: Multiple cython sources found for extension '%s': %s\n"
+ u"See https://cython.readthedocs.io/en/latest/src/userguide/sharing_declarations.html "
+ u"for sharing declarations among Cython files." % (pattern.name, cython_sources))
else:
# ignore non-cython modules
module_list.append(pattern)
@@ -687,7 +809,6 @@
template = pattern
name = template.name
base = DistutilsInfo(exn=template)
- exn_type = template.__class__
ext_language = None # do not override whatever the Extension says
else:
msg = str("pattern is not of type str nor subclass of Extension (%s)"
@@ -699,16 +820,15 @@
for file in nonempty(sorted(extended_iglob(filepattern)), "'%s' doesn't match any files" % filepattern):
if os.path.abspath(file) in to_exclude:
continue
- pkg = deps.package(file)
module_name = deps.fully_qualified_name(file)
if '*' in name:
if module_name in explicit_modules:
continue
- elif name != module_name:
- print("Warning: Extension name '%s' does not match fully qualified name '%s' of '%s'" % (
- name, module_name, file))
+ elif name:
module_name = name
+ Utils.raise_error_if_module_name_forbidden(module_name)
+
if module_name not in seen:
try:
kwds = deps.distutils_info(file, aliases, base).values
@@ -721,83 +841,146 @@
if key not in kwds:
kwds[key] = value
- sources = [file]
- if template is not None:
- sources += [m for m in template.sources if m != filepattern]
+ kwds['name'] = module_name
+
+ sources = [file] + [m for m in template.sources if m != filepattern]
if 'sources' in kwds:
# allow users to add .c files etc.
for source in kwds['sources']:
source = encode_filename_in_py2(source)
if source not in sources:
sources.append(source)
- extra_sources = kwds['sources']
- del kwds['sources']
- else:
- extra_sources = None
- if 'depends' in kwds:
- depends = resolve_depends(kwds['depends'], (kwds.get('include_dirs') or []) + ["."])
- if template is not None:
- # Always include everything from the template.
- depends = set(template.depends).union(depends)
- # Sort depends to make the metadata dump in the
- # Cython-generated C code predictable.
- kwds['depends'] = sorted(depends)
+ kwds['sources'] = sources
if ext_language and 'language' not in kwds:
kwds['language'] = ext_language
- module_list.append(exn_type(
- name=module_name,
- sources=sources,
- **kwds))
- if extra_sources:
- kwds['sources'] = extra_sources
- module_metadata[module_name] = {'distutils': kwds, 'module_name': module_name}
- m = module_list[-1]
+ np_pythran = kwds.pop('np_pythran', False)
+
+ # Create the new extension
+ m, metadata = create_extension(template, kwds)
+ m.np_pythran = np_pythran or getattr(m, 'np_pythran', False)
+ if m.np_pythran:
+ update_pythran_extension(m)
+ module_list.append(m)
+
+ # Store metadata (this will be written as JSON in the
+ # generated C file but otherwise has no purpose)
+ module_metadata[module_name] = metadata
+
+ if file not in m.sources:
+ # Old setuptools unconditionally replaces .pyx with .c/.cpp
+ target_file = os.path.splitext(file)[0] + ('.cpp' if m.language == 'c++' else '.c')
+ try:
+ m.sources.remove(target_file)
+ except ValueError:
+ # never seen this in the wild, but probably better to warn about this unexpected case
+ print(u"Warning: Cython source file not found in sources list, adding %s" % file)
+ m.sources.insert(0, file)
seen.add(name)
return module_list, module_metadata
# This is the user-exposed entry point.
-def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False, force=False, language=None,
- exclude_failures=False, **options):
+def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False, force=None, language=None,
+ exclude_failures=False, show_all_warnings=False, **options):
"""
Compile a set of source modules into C/C++ files and return a list of distutils
Extension objects for them.
- As module list, pass either a glob pattern, a list of glob patterns or a list of
- Extension objects. The latter allows you to configure the extensions separately
- through the normal distutils options.
-
- When using glob patterns, you can exclude certain module names explicitly
- by passing them into the 'exclude' option.
-
- To globally enable C++ mode, you can pass language='c++'. Otherwise, this
- will be determined at a per-file level based on compiler directives. This
- affects only modules found based on file names. Extension instances passed
- into cythonize() will not be changed.
-
- For parallel compilation, set the 'nthreads' option to the number of
- concurrent builds.
-
- For a broad 'try to compile' mode that ignores compilation failures and
- simply excludes the failed extensions, pass 'exclude_failures=True'. Note
- that this only really makes sense for compiling .py files which can also
- be used without compilation.
+ :param module_list: As module list, pass either a glob pattern, a list of glob
+ patterns or a list of Extension objects. The latter
+ allows you to configure the extensions separately
+ through the normal distutils options.
+ You can also pass Extension objects that have
+ glob patterns as their sources. Then, cythonize
+ will resolve the pattern and create a
+ copy of the Extension for every matching file.
+
+ :param exclude: When passing glob patterns as ``module_list``, you can exclude certain
+ module names explicitly by passing them into the ``exclude`` option.
+
+ :param nthreads: The number of concurrent builds for parallel compilation
+ (requires the ``multiprocessing`` module).
+
+ :param aliases: If you want to use compiler directives like ``# distutils: ...`` but
+ can only know at compile time (when running the ``setup.py``) which values
+ to use, you can use aliases and pass a dictionary mapping those aliases
+ to Python strings when calling :func:`cythonize`. As an example, say you
+ want to use the compiler
+ directive ``# distutils: include_dirs = ../static_libs/include/``
+ but this path isn't always fixed and you want to find it when running
+ the ``setup.py``. You can then do ``# distutils: include_dirs = MY_HEADERS``,
+ find the value of ``MY_HEADERS`` in the ``setup.py``, put it in a python
+ variable called ``foo`` as a string, and then call
+ ``cythonize(..., aliases={'MY_HEADERS': foo})``.
+
+ :param quiet: If True, Cython won't print error, warning, or status messages during the
+ compilation.
+
+ :param force: Forces the recompilation of the Cython modules, even if the timestamps
+ don't indicate that a recompilation is necessary.
+
+ :param language: To globally enable C++ mode, you can pass ``language='c++'``. Otherwise, this
+ will be determined at a per-file level based on compiler directives. This
+ affects only modules found based on file names. Extension instances passed
+ into :func:`cythonize` will not be changed. It is recommended to rather
+ use the compiler directive ``# distutils: language = c++`` than this option.
+
+ :param exclude_failures: For a broad 'try to compile' mode that ignores compilation
+ failures and simply excludes the failed extensions,
+ pass ``exclude_failures=True``. Note that this only
+ really makes sense for compiling ``.py`` files which can also
+ be used without compilation.
+
+ :param show_all_warnings: By default, not all Cython warnings are printed.
+ Set to true to show all warnings.
+
+ :param annotate: If ``True``, will produce a HTML file for each of the ``.pyx`` or ``.py``
+ files compiled. The HTML file gives an indication
+ of how much Python interaction there is in
+ each of the source code lines, compared to plain C code.
+ It also allows you to see the C/C++ code
+ generated for each line of Cython code. This report is invaluable when
+ optimizing a function for speed,
+ and for determining when to :ref:`release the GIL `:
+ in general, a ``nogil`` block may contain only "white" code.
+ See examples in :ref:`determining_where_to_add_types` or
+ :ref:`primes`.
+
+
+ :param annotate-fullc: If ``True`` will produce a colorized HTML version of
+ the source which includes entire generated C/C++-code.
+
+
+ :param compiler_directives: Allow to set compiler directives in the ``setup.py`` like this:
+ ``compiler_directives={'embedsignature': True}``.
+ See :ref:`compiler-directives`.
- Additional compilation options can be passed as keyword arguments.
+ :param depfile: produce depfiles for the sources if True.
"""
if exclude is None:
exclude = []
if 'include_path' not in options:
options['include_path'] = ['.']
if 'common_utility_include_dir' in options:
- if options.get('cache'):
- raise NotImplementedError("common_utility_include_dir does not yet work with caching")
safe_makedirs(options['common_utility_include_dir'])
+
+ depfile = options.pop('depfile', None)
+
+ if pythran is None:
+ pythran_options = None
+ else:
+ pythran_options = CompilationOptions(**options)
+ pythran_options.cplus = True
+ pythran_options.np_pythran = True
+
+ if force is None:
+ force = os.environ.get("CYTHON_FORCE_REGEN") == "1" # allow global overrides for build systems
+
c_options = CompilationOptions(**options)
cpp_options = CompilationOptions(**options); cpp_options.cplus = True
- ctx = c_options.create_context()
+ ctx = Context.from_options(c_options)
options = c_options
module_list, module_metadata = create_extension_list(
module_list,
@@ -807,30 +990,47 @@
exclude_failures=exclude_failures,
language=language,
aliases=aliases)
+
+ fix_windows_unicode_modules(module_list)
+
deps = create_dependency_tree(ctx, quiet=quiet)
build_dir = getattr(options, 'build_dir', None)
- modules_by_cfile = {}
+ def copy_to_build_dir(filepath, root=os.getcwd()):
+ filepath_abs = os.path.abspath(filepath)
+ if os.path.isabs(filepath):
+ filepath = filepath_abs
+ if filepath_abs.startswith(root):
+ # distutil extension depends are relative to cwd
+ mod_dir = join_path(build_dir,
+ os.path.dirname(_relpath(filepath, root)))
+ copy_once_if_newer(filepath_abs, mod_dir)
+
+ modules_by_cfile = collections.defaultdict(list)
to_compile = []
for m in module_list:
if build_dir:
- root = os.getcwd() # distutil extension depends are relative to cwd
- def copy_to_build_dir(filepath, root=root):
- filepath_abs = os.path.abspath(filepath)
- if os.path.isabs(filepath):
- filepath = filepath_abs
- if filepath_abs.startswith(root):
- mod_dir = join_path(build_dir,
- os.path.dirname(_relpath(filepath, root)))
- copy_once_if_newer(filepath_abs, mod_dir)
for dep in m.depends:
copy_to_build_dir(dep)
+ cy_sources = [
+ source for source in m.sources
+ if os.path.splitext(source)[1] in ('.pyx', '.py')]
+ if len(cy_sources) == 1:
+ # normal "special" case: believe the Extension module name to allow user overrides
+ full_module_name = m.name
+ else:
+ # infer FQMN from source files
+ full_module_name = None
+
new_sources = []
for source in m.sources:
base, ext = os.path.splitext(source)
if ext in ('.pyx', '.py'):
- if m.language == 'c++':
+ if m.np_pythran:
+ c_file = base + '.cpp'
+ options = pythran_options
+ elif m.language == 'c++':
c_file = base + '.cpp'
options = cpp_options
else:
@@ -839,11 +1039,34 @@
# setup for out of place build directory if enabled
if build_dir:
+ if os.path.isabs(c_file):
+ c_file = os.path.splitdrive(c_file)[1]
+ c_file = c_file.split(os.sep, 1)[1]
c_file = os.path.join(build_dir, c_file)
dir = os.path.dirname(c_file)
safe_makedirs_once(dir)
- if os.path.exists(c_file):
+ # write out the depfile, if requested
+ if depfile:
+ dependencies = deps.all_dependencies(source)
+ src_base_dir, _ = os.path.split(source)
+ if not src_base_dir.endswith(os.sep):
+ src_base_dir += os.sep
+ # paths below the base_dir are relative, otherwise absolute
+ paths = []
+ for fname in dependencies:
+ if fname.startswith(src_base_dir):
+ paths.append(os.path.relpath(fname, src_base_dir))
+ else:
+ paths.append(os.path.abspath(fname))
+
+ depline = os.path.split(c_file)[1] + ": \\\n "
+ depline += " \\\n ".join(paths) + "\n"
+ with open(c_file+'.dep', 'w') as outfile:
+ outfile.write(depline)
+
+ # Missing files and those generated by other Cython versions should always be recreated.
+ if Utils.file_generated_by_this_cython(c_file):
c_timestamp = os.path.getmtime(c_file)
else:
c_timestamp = -1
@@ -857,23 +1080,24 @@
dep_timestamp, dep = deps.newest_dependency(source)
priority = 2 - (dep in deps.immediate_dependencies(source))
if force or c_timestamp < dep_timestamp:
- if not quiet:
+ if not quiet and not force:
if source == dep:
- print("Compiling %s because it changed." % source)
+ print(u"Compiling %s because it changed." % Utils.decode_filename(source))
else:
- print("Compiling %s because it depends on %s." % (source, dep))
+ print(u"Compiling %s because it depends on %s." % (
+ Utils.decode_filename(source),
+ Utils.decode_filename(dep),
+ ))
if not force and options.cache:
- extra = m.language
- fingerprint = deps.transitive_fingerprint(source, extra)
+ fingerprint = deps.transitive_fingerprint(source, m, options)
else:
fingerprint = None
- to_compile.append((priority, source, c_file, fingerprint, quiet,
- options, not exclude_failures, module_metadata.get(m.name)))
+ to_compile.append((
+ priority, source, c_file, fingerprint, quiet,
+ options, not exclude_failures, module_metadata.get(m.name),
+ full_module_name, show_all_warnings))
new_sources.append(c_file)
- if c_file not in modules_by_cfile:
- modules_by_cfile[c_file] = [m]
- else:
- modules_by_cfile[c_file].append(m)
+ modules_by_cfile[c_file].append(m)
else:
new_sources.append(source)
if build_dir:
@@ -895,32 +1119,26 @@
if N <= 1:
nthreads = 0
if nthreads:
- # Requires multiprocessing (or Python >= 2.6)
+ import multiprocessing
+ pool = multiprocessing.Pool(
+ nthreads, initializer=_init_multiprocessing_helper)
+ # This is a bit more involved than it should be, because KeyboardInterrupts
+ # break the multiprocessing workers when using a normal pool.map().
+ # See, for example:
+ # https://noswap.com/blog/python-multiprocessing-keyboardinterrupt
try:
- import multiprocessing
- pool = multiprocessing.Pool(
- nthreads, initializer=_init_multiprocessing_helper)
- except (ImportError, OSError):
- print("multiprocessing required for parallel cythonization")
- nthreads = 0
- else:
- # This is a bit more involved than it should be, because KeyboardInterrupts
- # break the multiprocessing workers when using a normal pool.map().
- # See, for example:
- # http://noswap.com/blog/python-multiprocessing-keyboardinterrupt
- try:
- result = pool.map_async(cythonize_one_helper, to_compile, chunksize=1)
- pool.close()
- while not result.ready():
- try:
- result.get(99999) # seconds
- except multiprocessing.TimeoutError:
- pass
- except KeyboardInterrupt:
- pool.terminate()
- raise
- pool.join()
- if not nthreads:
+ result = pool.map_async(cythonize_one_helper, to_compile, chunksize=1)
+ pool.close()
+ while not result.ready():
+ try:
+ result.get(99999) # seconds
+ except multiprocessing.TimeoutError:
+ pass
+ except KeyboardInterrupt:
+ pool.terminate()
+ raise
+ pool.join()
+ else:
for args in to_compile:
cythonize_one(*args)
@@ -940,7 +1158,7 @@
if failed_modules:
for module in failed_modules:
module_list.remove(module)
- print("Failed compilations: %s" % ', '.join(sorted([
+ print(u"Failed compilations: %s" % ', '.join(sorted([
module.name for module in failed_modules])))
if options.cache:
@@ -951,6 +1169,41 @@
return module_list
+def fix_windows_unicode_modules(module_list):
+ # Hack around a distutils 3.[5678] bug on Windows for unicode module names.
+ # https://bugs.python.org/issue39432
+ if sys.platform != "win32":
+ return
+ if sys.version_info < (3, 5) or sys.version_info >= (3, 8, 2):
+ return
+
+ def make_filtered_list(ignored_symbol, old_entries):
+ class FilteredExportSymbols(list):
+ # export_symbols for unicode filename cause link errors on Windows
+ # Cython doesn't need them (it already defines PyInit with the correct linkage)
+ # so use this class as a temporary fix to stop them from being generated
+ def __contains__(self, val):
+ # so distutils doesn't "helpfully" add PyInit_
+ return val == ignored_symbol or list.__contains__(self, val)
+
+ filtered_list = FilteredExportSymbols(old_entries)
+ if old_entries:
+ filtered_list.extend(name for name in old_entries if name != ignored_symbol)
+ return filtered_list
+
+ for m in module_list:
+ # TODO: use m.name.isascii() in Py3.7+
+ try:
+ m.name.encode("ascii")
+ continue
+ except UnicodeEncodeError:
+ pass
+ m.export_symbols = make_filtered_list(
+ "PyInit_" + m.name.rsplit(".", 1)[-1],
+ m.export_symbols,
+ )
+
+
if os.environ.get('XML_RESULTS'):
compile_result_dir = os.environ['XML_RESULTS']
def record_results(func):
@@ -989,45 +1242,51 @@
# TODO: Share context? Issue: pyx processing leaks into pxd module
@record_results
-def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True, embedded_metadata=None, progress=""):
- from ..Compiler.Main import compile, default_options
+def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None,
+ raise_on_failure=True, embedded_metadata=None,
+ full_module_name=None, show_all_warnings=False,
+ progress=""):
+ from ..Compiler.Main import compile_single, default_options
from ..Compiler.Errors import CompileError, PyrexError
if fingerprint:
if not os.path.exists(options.cache):
- try:
- os.mkdir(options.cache)
- except:
- if not os.path.exists(options.cache):
- raise
+ safe_makedirs(options.cache)
# Cython-generated c files are highly compressible.
# (E.g. a compression ratio of about 10 for Sage).
- fingerprint_file = join_path(
- options.cache, "%s-%s%s" % (os.path.basename(c_file), fingerprint, gzip_ext))
- if os.path.exists(fingerprint_file):
+ fingerprint_file_base = join_path(
+ options.cache, "%s-%s" % (os.path.basename(c_file), fingerprint))
+ gz_fingerprint_file = fingerprint_file_base + gzip_ext
+ zip_fingerprint_file = fingerprint_file_base + '.zip'
+ if os.path.exists(gz_fingerprint_file) or os.path.exists(zip_fingerprint_file):
if not quiet:
- print("%sFound compiled %s in cache" % (progress, pyx_file))
- os.utime(fingerprint_file, None)
- g = gzip_open(fingerprint_file, 'rb')
- try:
- f = open(c_file, 'wb')
- try:
- shutil.copyfileobj(g, f)
- finally:
- f.close()
- finally:
- g.close()
+ print(u"%sFound compiled %s in cache" % (progress, pyx_file))
+ if os.path.exists(gz_fingerprint_file):
+ os.utime(gz_fingerprint_file, None)
+ with contextlib.closing(gzip_open(gz_fingerprint_file, 'rb')) as g:
+ with contextlib.closing(open(c_file, 'wb')) as f:
+ shutil.copyfileobj(g, f)
+ else:
+ os.utime(zip_fingerprint_file, None)
+ dirname = os.path.dirname(c_file)
+ with contextlib.closing(zipfile.ZipFile(zip_fingerprint_file)) as z:
+ for artifact in z.namelist():
+ z.extract(artifact, os.path.join(dirname, artifact))
return
if not quiet:
- print("%sCythonizing %s" % (progress, pyx_file))
+ print(u"%sCythonizing %s" % (progress, Utils.decode_filename(pyx_file)))
if options is None:
options = CompilationOptions(default_options)
options.output_file = c_file
options.embedded_metadata = embedded_metadata
+ old_warning_level = Errors.LEVEL
+ if show_all_warnings:
+ Errors.LEVEL = 0
+
any_failures = 0
try:
- result = compile([pyx_file], options)
+ result = compile_single(pyx_file, options, full_module_name=full_module_name)
if result.num_errors > 0:
any_failures = 1
except (EnvironmentError, PyrexError) as e:
@@ -1042,21 +1301,31 @@
import traceback
traceback.print_exc()
any_failures = 1
+ finally:
+ if show_all_warnings:
+ Errors.LEVEL = old_warning_level
+
if any_failures:
if raise_on_failure:
raise CompileError(None, pyx_file)
elif os.path.exists(c_file):
os.remove(c_file)
elif fingerprint:
- f = open(c_file, 'rb')
- try:
- g = gzip_open(fingerprint_file, 'wb')
- try:
- shutil.copyfileobj(f, g)
- finally:
- g.close()
- finally:
- f.close()
+ artifacts = list(filter(None, [
+ getattr(result, attr, None)
+ for attr in ('c_file', 'h_file', 'api_file', 'i_file')]))
+ if len(artifacts) == 1:
+ fingerprint_file = gz_fingerprint_file
+ with contextlib.closing(open(c_file, 'rb')) as f:
+ with contextlib.closing(gzip_open(fingerprint_file + '.tmp', 'wb')) as g:
+ shutil.copyfileobj(f, g)
+ else:
+ fingerprint_file = zip_fingerprint_file
+ with contextlib.closing(zipfile.ZipFile(
+ fingerprint_file + '.tmp', 'w', zipfile_compression_mode)) as zip:
+ for artifact in artifacts:
+ zip.write(artifact, os.path.basename(artifact))
+ os.rename(fingerprint_file + '.tmp', fingerprint_file)
def cythonize_one_helper(m):
@@ -1077,9 +1346,10 @@
def cleanup_cache(cache, target_size, ratio=.85):
try:
p = subprocess.Popen(['du', '-s', '-k', os.path.abspath(cache)], stdout=subprocess.PIPE)
+ stdout, _ = p.communicate()
res = p.wait()
if res == 0:
- total_size = 1024 * int(p.stdout.read().strip().split()[0])
+ total_size = 1024 * int(stdout.strip().split()[0])
if total_size < target_size:
return
except (OSError, ValueError):
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Inline.py cython-0.20.1+1~202203241016-9537/Cython/Build/Inline.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Inline.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Inline.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,32 +1,32 @@
from __future__ import absolute_import
-import sys, os, re, inspect
-import imp
-
-try:
- import hashlib
-except ImportError:
- import md5 as hashlib
+import hashlib
+import inspect
+import os
+import re
+import sys
from distutils.core import Distribution, Extension
from distutils.command.build_ext import build_ext
import Cython
-from ..Compiler.Main import Context, CompilationOptions, default_options
+from ..Compiler.Main import Context
+from ..Compiler.Options import default_options
-from ..Compiler.ParseTreeTransforms import (CythonTransform,
- SkipDeclarations, AnalyseDeclarationsTransform, EnvTransform)
+from ..Compiler.Visitor import CythonTransform, EnvTransform
+from ..Compiler.ParseTreeTransforms import SkipDeclarations
from ..Compiler.TreeFragment import parse_from_strings
from ..Compiler.StringEncoding import _unicode
from .Dependencies import strip_string_literals, cythonize, cached_function
-from ..Compiler import Pipeline, Nodes
+from ..Compiler import Pipeline
from ..Utils import get_cython_cache_dir
import cython as cython_module
-IS_PY3 = sys.version_info >= (3, 0)
+
+IS_PY3 = sys.version_info >= (3,)
# A utility function to convert user-supplied ASCII strings to unicode.
-if sys.version_info[0] < 3:
+if not IS_PY3:
def to_unicode(s):
if isinstance(s, bytes):
return s.decode('ascii')
@@ -36,6 +36,20 @@
to_unicode = lambda x: x
+if sys.version_info < (3, 5):
+ import imp
+ def load_dynamic(name, module_path):
+ return imp.load_dynamic(name, module_path)
+else:
+ import importlib.util as _importlib_util
+ def load_dynamic(name, module_path):
+ spec = _importlib_util.spec_from_file_location(name, module_path)
+ module = _importlib_util.module_from_spec(spec)
+ # sys.modules[name] = module
+ spec.loader.exec_module(module)
+ return module
+
+
class UnboundSymbols(EnvTransform, SkipDeclarations):
def __init__(self):
CythonTransform.__init__(self, None)
@@ -90,7 +104,7 @@
elif 'numpy' in sys.modules and isinstance(arg, sys.modules['numpy'].ndarray):
return 'numpy.ndarray[numpy.%s_t, ndim=%s]' % (arg.dtype.name, arg.ndim)
else:
- for base_type in py_type.mro():
+ for base_type in py_type.__mro__:
if base_type.__module__ in ('__builtin__', 'builtins'):
return 'object'
module = context.find_module(base_type.__module__, need_pxd=False)
@@ -120,6 +134,7 @@
_cython_inline_cache = {}
_cython_inline_default_context = _create_context(('.',))
+
def _populate_unbound(kwds, unbound_symbols, locals=None, globals=None):
for symbol in unbound_symbols:
if symbol not in kwds:
@@ -136,20 +151,37 @@
else:
print("Couldn't find %r" % symbol)
-def cython_inline(code, get_type=unsafe_type, lib_dir=os.path.join(get_cython_cache_dir(), 'inline'),
- cython_include_dirs=None, force=False, quiet=False, locals=None, globals=None, **kwds):
+
+def _inline_key(orig_code, arg_sigs, language_level):
+ key = orig_code, arg_sigs, sys.version_info, sys.executable, language_level, Cython.__version__
+ return hashlib.sha1(_unicode(key).encode('utf-8')).hexdigest()
+
+
+def cython_inline(code, get_type=unsafe_type,
+ lib_dir=os.path.join(get_cython_cache_dir(), 'inline'),
+ cython_include_dirs=None, cython_compiler_directives=None,
+ force=False, quiet=False, locals=None, globals=None, language_level=None, **kwds):
if get_type is None:
get_type = lambda x: 'object'
ctx = _create_context(tuple(cython_include_dirs)) if cython_include_dirs else _cython_inline_default_context
+ cython_compiler_directives = dict(cython_compiler_directives) if cython_compiler_directives else {}
+ if language_level is None and 'language_level' not in cython_compiler_directives:
+ language_level = '3str'
+ if language_level is not None:
+ cython_compiler_directives['language_level'] = language_level
+
+ key_hash = None
+
# Fast path if this has been called in this session.
_unbound_symbols = _cython_inline_cache.get(code)
if _unbound_symbols is not None:
_populate_unbound(kwds, _unbound_symbols, locals, globals)
args = sorted(kwds.items())
arg_sigs = tuple([(get_type(value, ctx), arg) for arg, value in args])
- invoke = _cython_inline_cache.get((code, arg_sigs))
+ key_hash = _inline_key(code, arg_sigs, language_level)
+ invoke = _cython_inline_cache.get((code, arg_sigs, key_hash))
if invoke is not None:
arg_list = [arg[1] for arg in args]
return invoke(*arg_list)
@@ -169,6 +201,7 @@
if not quiet:
# Parsing from strings not fully supported (e.g. cimports).
print("Could not parse code as a string (to extract unbound symbols).")
+
cimports = []
for name, arg in list(kwds.items()):
if arg is cython_module:
@@ -176,8 +209,9 @@
del kwds[name]
arg_names = sorted(kwds)
arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names])
- key = orig_code, arg_sigs, sys.version_info, sys.executable, Cython.__version__
- module_name = "_cython_inline_" + hashlib.md5(_unicode(key).encode('utf-8')).hexdigest()
+ if key_hash is None:
+ key_hash = _inline_key(orig_code, arg_sigs, language_level)
+ module_name = "_cython_inline_" + key_hash
if module_name in sys.modules:
module = sys.modules[module_name]
@@ -195,6 +229,7 @@
os.makedirs(lib_dir)
if force or not os.path.isfile(module_path):
cflags = []
+ define_macros = []
c_include_dirs = []
qualified = re.compile(r'([.\w]+)[.]')
for type, _ in arg_sigs:
@@ -205,6 +240,7 @@
if m.groups()[0] == 'numpy':
import numpy
c_include_dirs.append(numpy.get_include())
+ define_macros.append(("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"))
# cflags.append('-Wno-unused')
module_body, func_body = extract_func_code(code)
params = ', '.join(['%s %s' % a for a in arg_sigs])
@@ -227,23 +263,30 @@
finally:
fh.close()
extension = Extension(
- name = module_name,
- sources = [pyx_file],
- include_dirs = c_include_dirs,
- extra_compile_args = cflags)
+ name=module_name,
+ sources=[pyx_file],
+ include_dirs=c_include_dirs or None,
+ extra_compile_args=cflags or None,
+ define_macros=define_macros or None,
+ )
if build_extension is None:
build_extension = _get_build_extension()
- build_extension.extensions = cythonize([extension], include_path=cython_include_dirs or ['.'], quiet=quiet)
+ build_extension.extensions = cythonize(
+ [extension],
+ include_path=cython_include_dirs or ['.'],
+ compiler_directives=cython_compiler_directives,
+ quiet=quiet)
build_extension.build_temp = os.path.dirname(pyx_file)
build_extension.build_lib = lib_dir
build_extension.run()
- module = imp.load_dynamic(module_name, module_path)
+ module = load_dynamic(module_name, module_path)
- _cython_inline_cache[orig_code, arg_sigs] = module.__invoke
+ _cython_inline_cache[orig_code, arg_sigs, key_hash] = module.__invoke
arg_list = [kwds[arg] for arg in arg_names]
return module.__invoke(*arg_list)
+
# Cached suffix used by cython_inline above. None should get
# overridden with actual value upon the first cython_inline invocation
cython_inline.so_ext = None
@@ -288,37 +331,6 @@
return '\n'.join(module), ' ' + '\n '.join(function)
-try:
- from inspect import getcallargs
-except ImportError:
- def getcallargs(func, *arg_values, **kwd_values):
- all = {}
- args, varargs, kwds, defaults = inspect.getargspec(func)
- if varargs is not None:
- all[varargs] = arg_values[len(args):]
- for name, value in zip(args, arg_values):
- all[name] = value
- for name, value in list(kwd_values.items()):
- if name in args:
- if name in all:
- raise TypeError("Duplicate argument %s" % name)
- all[name] = kwd_values.pop(name)
- if kwds is not None:
- all[kwds] = kwd_values
- elif kwd_values:
- raise TypeError("Unexpected keyword arguments: %s" % list(kwd_values))
- if defaults is None:
- defaults = ()
- first_default = len(args) - len(defaults)
- for ix, name in enumerate(args):
- if name not in all:
- if ix >= first_default:
- all[name] = defaults[ix - first_default]
- else:
- raise TypeError("Missing argument: %s" % name)
- return all
-
-
def get_body(source):
ix = source.index(':')
if source[:5] == 'lambda':
@@ -336,7 +348,7 @@
self._body = get_body(inspect.getsource(f))
def __call__(self, *args, **kwds):
- all = getcallargs(self._f, *args, **kwds)
+ all = inspect.getcallargs(self._f, *args, **kwds)
if IS_PY3:
return cython_inline(self._body, locals=self._f.__globals__, globals=self._f.__globals__, **all)
else:
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/ipython-COPYING.rst cython-0.20.1+1~202203241016-9537/Cython/Build/ipython-COPYING.rst
--- cython-0.20.1+1~201611251650-6686/Cython/Build/ipython-COPYING.rst 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/ipython-COPYING.rst 2022-03-24 10:16:46.000000000 +0000
@@ -65,7 +65,7 @@
their copyright in the commit message of the change, when they commit the
change to one of the IPython repositories.
-With this in mind, the following banner should be used in any source code file
+With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:
::
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/IpythonMagic.py cython-0.20.1+1~202203241016-9537/Cython/Build/IpythonMagic.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/IpythonMagic.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/IpythonMagic.py 2022-03-24 10:16:46.000000000 +0000
@@ -14,7 +14,7 @@
Usage
=====
-To enable the magics below, execute ``%load_ext cythonmagic``.
+To enable the magics below, execute ``%load_ext cython``.
``%%cython``
@@ -52,24 +52,20 @@
import re
import sys
import time
+import copy
+import distutils.log
+import textwrap
-try:
- reload
-except NameError: # Python 3
- from imp import reload
-
-try:
- import hashlib
-except ImportError:
- import md5 as hashlib
+IO_ENCODING = sys.getfilesystemencoding()
+IS_PY2 = sys.version_info[0] < 3
+import hashlib
from distutils.core import Distribution, Extension
from distutils.command.build_ext import build_ext
from IPython.core import display
from IPython.core import magic_arguments
from IPython.core.magic import Magics, magics_class, cell_magic
-from IPython.utils import py3compat
try:
from IPython.paths import get_ipython_cache_dir
except ImportError:
@@ -81,21 +77,53 @@
from ..Compiler.Errors import CompileError
from .Inline import cython_inline
from .Dependencies import cythonize
+from ..Utils import captured_fd, print_captured
+
+
+PGO_CONFIG = {
+ 'gcc': {
+ 'gen': ['-fprofile-generate', '-fprofile-dir={TEMPDIR}'],
+ 'use': ['-fprofile-use', '-fprofile-correction', '-fprofile-dir={TEMPDIR}'],
+ },
+ # blind copy from 'configure' script in CPython 3.7
+ 'icc': {
+ 'gen': ['-prof-gen'],
+ 'use': ['-prof-use'],
+ }
+}
+PGO_CONFIG['mingw32'] = PGO_CONFIG['gcc']
+
+
+if IS_PY2:
+ def encode_fs(name):
+ return name if isinstance(name, bytes) else name.encode(IO_ENCODING)
+else:
+ def encode_fs(name):
+ return name
@magics_class
class CythonMagics(Magics):
def __init__(self, shell):
- super(CythonMagics,self).__init__(shell)
+ super(CythonMagics, self).__init__(shell)
self._reloads = {}
self._code_cache = {}
self._pyximport_installed = False
def _import_all(self, module):
- for k,v in module.__dict__.items():
- if not k.startswith('__'):
- self.shell.push({k:v})
+ mdict = module.__dict__
+ if '__all__' in mdict:
+ keys = mdict['__all__']
+ else:
+ keys = [k for k in mdict if not k.startswith('_')]
+
+ for k in keys:
+ try:
+ self.shell.push({k: mdict[k]})
+ except KeyError:
+ msg = "'module' object has no attribute '%s'" % k
+ raise AttributeError(msg)
@cell_magic
def cython_inline(self, line, cell):
@@ -139,11 +167,15 @@
f.write(cell)
if 'pyximport' not in sys.modules or not self._pyximport_installed:
import pyximport
- pyximport.install(reload_support=True)
+ pyximport.install()
self._pyximport_installed = True
if module_name in self._reloads:
module = self._reloads[module_name]
- reload(module)
+ # Note: reloading extension modules is not actually supported
+ # (requires PEP-489 reinitialisation support).
+ # Don't know why this should ever have worked as it reads here.
+ # All we really need to do is to update the globals below.
+ #reload(module)
else:
__import__(module_name)
module = sys.modules[module_name]
@@ -152,6 +184,19 @@
@magic_arguments.magic_arguments()
@magic_arguments.argument(
+ '-a', '--annotate', action='store_const', const='default', dest='annotate',
+ help="Produce a colorized HTML version of the source."
+ )
+ @magic_arguments.argument(
+ '--annotate-fullc', action='store_const', const='fullc', dest='annotate',
+ help="Produce a colorized HTML version of the source "
+ "which includes entire generated C/C++-code."
+ )
+ @magic_arguments.argument(
+ '-+', '--cplus', action='store_true', default=False,
+ help="Output a C++ rather than C file."
+ )
+ @magic_arguments.argument(
'-3', dest='language_level', action='store_const', const=3, default=None,
help="Select Python 3 syntax."
)
@@ -160,6 +205,11 @@
help="Select Python 2 syntax."
)
@magic_arguments.argument(
+ '-f', '--force', action='store_true', default=False,
+ help="Force the compilation of a new module, even if the source has been "
+ "previously compiled."
+ )
+ @magic_arguments.argument(
'-c', '--compile-args', action='append', default=[],
help="Extra flags to pass to compiler via the `extra_compile_args` "
"Extension flag (can be specified multiple times)."
@@ -189,17 +239,19 @@
"multiple times)."
)
@magic_arguments.argument(
- '-+', '--cplus', action='store_true', default=False,
- help="Output a C++ rather than C file."
+ '-S', '--src', action='append', default=[],
+ help="Add a path to the list of src files (can be specified "
+ "multiple times)."
)
@magic_arguments.argument(
- '-f', '--force', action='store_true', default=False,
- help="Force the compilation of a new module, even if the source has been "
- "previously compiled."
+ '--pgo', dest='pgo', action='store_true', default=False,
+ help=("Enable profile guided optimisation in the C compiler. "
+ "Compiles the cell twice and executes it in between to generate a runtime profile.")
)
@magic_arguments.argument(
- '-a', '--annotate', action='store_true', default=False,
- help="Produce a colorized HTML version of the source."
+ '--verbose', dest='quiet', action='store_false', default=True,
+ help=("Print debug information like generated .c/.cpp file location "
+ "and exact gcc/g++ command invoked.")
)
@cell_magic
def cython(self, line, cell):
@@ -221,76 +273,90 @@
%%cython --compile-args=-fopenmp --link-args=-fopenmp
...
+
+ To enable profile guided optimisation, pass the ``--pgo`` option.
+ Note that the cell itself needs to take care of establishing a suitable
+ profile when executed. This can be done by implementing the functions to
+ optimise, and then calling them directly in the same cell on some realistic
+ training data like this::
+
+ %%cython --pgo
+ def critical_function(data):
+ for item in data:
+ ...
+
+ # execute function several times to build profile
+ from somewhere import some_typical_data
+ for _ in range(100):
+ critical_function(some_typical_data)
+
+ In Python 3.5 and later, you can distinguish between the profile and
+ non-profile runs as follows::
+
+ if "_pgo_" in __name__:
+ ... # execute critical code here
"""
args = magic_arguments.parse_argstring(self.cython, line)
- code = cell if cell.endswith('\n') else cell+'\n'
+ code = cell if cell.endswith('\n') else cell + '\n'
lib_dir = os.path.join(get_ipython_cache_dir(), 'cython')
- quiet = True
- key = code, line, sys.version_info, sys.executable, cython_version
+ key = (code, line, sys.version_info, sys.executable, cython_version)
if not os.path.exists(lib_dir):
os.makedirs(lib_dir)
+ if args.pgo:
+ key += ('pgo',)
if args.force:
# Force a new module name by adding the current time to the
# key which is hashed to determine the module name.
- key += time.time(),
+ key += (time.time(),)
if args.name:
- module_name = py3compat.unicode_to_str(args.name)
+ module_name = str(args.name) # no-op in Py3
else:
- module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
+ module_name = "_cython_magic_" + hashlib.sha1(str(key).encode('utf-8')).hexdigest()
+ html_file = os.path.join(lib_dir, module_name + '.html')
module_path = os.path.join(lib_dir, module_name + self.so_ext)
have_module = os.path.isfile(module_path)
- need_cythonize = not have_module
+ need_cythonize = args.pgo or not have_module
if args.annotate:
- html_file = os.path.join(lib_dir, module_name + '.html')
if not os.path.isfile(html_file):
need_cythonize = True
+ extension = None
if need_cythonize:
- c_include_dirs = args.include
- if 'numpy' in code:
- import numpy
- c_include_dirs.append(numpy.get_include())
- pyx_file = os.path.join(lib_dir, module_name + '.pyx')
- pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
- with io.open(pyx_file, 'w', encoding='utf-8') as f:
- f.write(code)
- extension = Extension(
- name = module_name,
- sources = [pyx_file],
- include_dirs = c_include_dirs,
- library_dirs = args.library_dirs,
- extra_compile_args = args.compile_args,
- extra_link_args = args.link_args,
- libraries = args.lib,
- language = 'c++' if args.cplus else 'c',
- )
- build_extension = self._get_build_extension()
- try:
- opts = dict(
- quiet=quiet,
- annotate=args.annotate,
- force=True,
- )
- if args.language_level is not None:
- assert args.language_level in (2, 3)
- opts['language_level'] = args.language_level
- elif sys.version_info[0] > 2:
- opts['language_level'] = 3
- build_extension.extensions = cythonize([extension], **opts)
- except CompileError:
- return
-
- if not have_module:
- build_extension.build_temp = os.path.dirname(pyx_file)
- build_extension.build_lib = lib_dir
- build_extension.run()
+ extensions = self._cythonize(module_name, code, lib_dir, args, quiet=args.quiet)
+ if extensions is None:
+ # Compilation failed and printed error message
+ return None
+ assert len(extensions) == 1
+ extension = extensions[0]
self._code_cache[key] = module_name
+ if args.pgo:
+ self._profile_pgo_wrapper(extension, lib_dir)
+
+ def print_compiler_output(stdout, stderr, where):
+ # On windows, errors are printed to stdout, we redirect both to sys.stderr.
+ print_captured(stdout, where, u"Content of stdout:\n")
+ print_captured(stderr, where, u"Content of stderr:\n")
+
+ get_stderr = get_stdout = None
+ try:
+ with captured_fd(1) as get_stdout:
+ with captured_fd(2) as get_stderr:
+ self._build_extension(
+ extension, lib_dir, pgo_step_name='use' if args.pgo else None, quiet=args.quiet)
+ except (distutils.errors.CompileError, distutils.errors.LinkError):
+ # Build failed, print error message from compiler/linker
+ print_compiler_output(get_stdout(), get_stderr(), sys.stderr)
+ return None
+
+ # Build seems ok, but we might still want to show any warnings that occurred
+ print_compiler_output(get_stdout(), get_stderr(), sys.stdout)
+
module = imp.load_dynamic(module_name, module_path)
self._import_all(module)
@@ -309,6 +375,128 @@
else:
return display.HTML(self.clean_annotated_html(annotated_html))
+ def _profile_pgo_wrapper(self, extension, lib_dir):
+ """
+ Generate a .c file for a separate extension module that calls the
+ module init function of the original module. This makes sure that the
+ PGO profiler sees the correct .o file of the final module, but it still
+ allows us to import the module under a different name for profiling,
+ before recompiling it into the PGO optimised module. Overwriting and
+ reimporting the same shared library is not portable.
+ """
+ extension = copy.copy(extension) # shallow copy, do not modify sources in place!
+ module_name = extension.name
+ pgo_module_name = '_pgo_' + module_name
+ pgo_wrapper_c_file = os.path.join(lib_dir, pgo_module_name + '.c')
+ with io.open(pgo_wrapper_c_file, 'w', encoding='utf-8') as f:
+ f.write(textwrap.dedent(u"""
+ #include "Python.h"
+ #if PY_MAJOR_VERSION < 3
+ extern PyMODINIT_FUNC init%(module_name)s(void);
+ PyMODINIT_FUNC init%(pgo_module_name)s(void); /*proto*/
+ PyMODINIT_FUNC init%(pgo_module_name)s(void) {
+ PyObject *sys_modules;
+ init%(module_name)s(); if (PyErr_Occurred()) return;
+ sys_modules = PyImport_GetModuleDict(); /* borrowed, no exception, "never" fails */
+ if (sys_modules) {
+ PyObject *module = PyDict_GetItemString(sys_modules, "%(module_name)s"); if (!module) return;
+ PyDict_SetItemString(sys_modules, "%(pgo_module_name)s", module);
+ Py_DECREF(module);
+ }
+ }
+ #else
+ extern PyMODINIT_FUNC PyInit_%(module_name)s(void);
+ PyMODINIT_FUNC PyInit_%(pgo_module_name)s(void); /*proto*/
+ PyMODINIT_FUNC PyInit_%(pgo_module_name)s(void) {
+ return PyInit_%(module_name)s();
+ }
+ #endif
+ """ % {'module_name': module_name, 'pgo_module_name': pgo_module_name}))
+
+ extension.sources = extension.sources + [pgo_wrapper_c_file] # do not modify in place!
+ extension.name = pgo_module_name
+
+ self._build_extension(extension, lib_dir, pgo_step_name='gen')
+
+ # import and execute module code to generate profile
+ so_module_path = os.path.join(lib_dir, pgo_module_name + self.so_ext)
+ imp.load_dynamic(pgo_module_name, so_module_path)
+
+ def _cythonize(self, module_name, code, lib_dir, args, quiet=True):
+ pyx_file = os.path.join(lib_dir, module_name + '.pyx')
+ pyx_file = encode_fs(pyx_file)
+
+ c_include_dirs = args.include
+ c_src_files = list(map(str, args.src))
+ if 'numpy' in code:
+ import numpy
+ c_include_dirs.append(numpy.get_include())
+ with io.open(pyx_file, 'w', encoding='utf-8') as f:
+ f.write(code)
+ extension = Extension(
+ name=module_name,
+ sources=[pyx_file] + c_src_files,
+ include_dirs=c_include_dirs,
+ library_dirs=args.library_dirs,
+ extra_compile_args=args.compile_args,
+ extra_link_args=args.link_args,
+ libraries=args.lib,
+ language='c++' if args.cplus else 'c',
+ )
+ try:
+ opts = dict(
+ quiet=quiet,
+ annotate=args.annotate,
+ force=True,
+ language_level=min(3, sys.version_info[0]),
+ )
+ if args.language_level is not None:
+ assert args.language_level in (2, 3)
+ opts['language_level'] = args.language_level
+ return cythonize([extension], **opts)
+ except CompileError:
+ return None
+
+ def _build_extension(self, extension, lib_dir, temp_dir=None, pgo_step_name=None, quiet=True):
+ build_extension = self._get_build_extension(
+ extension, lib_dir=lib_dir, temp_dir=temp_dir, pgo_step_name=pgo_step_name)
+ old_threshold = None
+ try:
+ if not quiet:
+ old_threshold = distutils.log.set_threshold(distutils.log.DEBUG)
+ build_extension.run()
+ finally:
+ if not quiet and old_threshold is not None:
+ distutils.log.set_threshold(old_threshold)
+
+ def _add_pgo_flags(self, build_extension, step_name, temp_dir):
+ compiler_type = build_extension.compiler.compiler_type
+ if compiler_type == 'unix':
+ compiler_cmd = build_extension.compiler.compiler_so
+ # TODO: we could try to call "[cmd] --version" for better insights
+ if not compiler_cmd:
+ pass
+ elif 'clang' in compiler_cmd or 'clang' in compiler_cmd[0]:
+ compiler_type = 'clang'
+ elif 'icc' in compiler_cmd or 'icc' in compiler_cmd[0]:
+ compiler_type = 'icc'
+ elif 'gcc' in compiler_cmd or 'gcc' in compiler_cmd[0]:
+ compiler_type = 'gcc'
+ elif 'g++' in compiler_cmd or 'g++' in compiler_cmd[0]:
+ compiler_type = 'gcc'
+ config = PGO_CONFIG.get(compiler_type)
+ orig_flags = []
+ if config and step_name in config:
+ flags = [f.format(TEMPDIR=temp_dir) for f in config[step_name]]
+ for extension in build_extension.extensions:
+ orig_flags.append((extension.extra_compile_args, extension.extra_link_args))
+ extension.extra_compile_args = extension.extra_compile_args + flags
+ extension.extra_link_args = extension.extra_link_args + flags
+ else:
+ print("No PGO %s configuration known for C compiler type '%s'" % (step_name, compiler_type),
+ file=sys.stderr)
+ return orig_flags
+
@property
def so_ext(self):
"""The extension suffix for compiled modules."""
@@ -330,7 +518,8 @@
else:
_path_created.clear()
- def _get_build_extension(self):
+ def _get_build_extension(self, extension=None, lib_dir=None, temp_dir=None,
+ pgo_step_name=None, _build_ext=build_ext):
self._clear_distutils_mkpath_cache()
dist = Distribution()
config_files = dist.find_config_files()
@@ -339,8 +528,28 @@
except ValueError:
pass
dist.parse_config_files(config_files)
- build_extension = build_ext(dist)
+
+ if not temp_dir:
+ temp_dir = lib_dir
+ add_pgo_flags = self._add_pgo_flags
+
+ if pgo_step_name:
+ base_build_ext = _build_ext
+ class _build_ext(_build_ext):
+ def build_extensions(self):
+ add_pgo_flags(self, pgo_step_name, temp_dir)
+ base_build_ext.build_extensions(self)
+
+ build_extension = _build_ext(dist)
build_extension.finalize_options()
+ if temp_dir:
+ temp_dir = encode_fs(temp_dir)
+ build_extension.build_temp = temp_dir
+ if lib_dir:
+ lib_dir = encode_fs(lib_dir)
+ build_extension.build_lib = lib_dir
+ if extension is not None:
+ build_extension.extensions = [extension]
return build_extension
@staticmethod
@@ -355,10 +564,10 @@
return html
__doc__ = __doc__.format(
- # rST doesn't see the -+ flag as part of an option list, so we
- # hide it from the module-level docstring.
- CYTHON_DOC = dedent(CythonMagics.cython.__doc__\
- .replace('-+, --cplus','--cplus ')),
- CYTHON_INLINE_DOC = dedent(CythonMagics.cython_inline.__doc__),
- CYTHON_PYXIMPORT_DOC = dedent(CythonMagics.cython_pyximport.__doc__),
+ # rST doesn't see the -+ flag as part of an option list, so we
+ # hide it from the module-level docstring.
+ CYTHON_DOC=dedent(CythonMagics.cython.__doc__\
+ .replace('-+, --cplus', '--cplus ')),
+ CYTHON_INLINE_DOC=dedent(CythonMagics.cython_inline.__doc__),
+ CYTHON_PYXIMPORT_DOC=dedent(CythonMagics.cython_pyximport.__doc__),
)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestCyCache.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestCyCache.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestCyCache.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestCyCache.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,116 @@
+import difflib
+import glob
+import gzip
+import os
+import tempfile
+
+import Cython.Build.Dependencies
+import Cython.Utils
+from Cython.TestUtils import CythonTest
+
+
+class TestCyCache(CythonTest):
+
+ def setUp(self):
+ CythonTest.setUp(self)
+ self.temp_dir = tempfile.mkdtemp(
+ prefix='cycache-test',
+ dir='TEST_TMP' if os.path.isdir('TEST_TMP') else None)
+ self.src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
+ self.cache_dir = tempfile.mkdtemp(prefix='cache', dir=self.temp_dir)
+
+ def cache_files(self, file_glob):
+ return glob.glob(os.path.join(self.cache_dir, file_glob))
+
+ def fresh_cythonize(self, *args, **kwargs):
+ Cython.Utils.clear_function_caches()
+ Cython.Build.Dependencies._dep_tree = None # discard method caches
+ Cython.Build.Dependencies.cythonize(*args, **kwargs)
+
+ def test_cycache_switch(self):
+ content1 = 'value = 1\n'
+ content2 = 'value = 2\n'
+ a_pyx = os.path.join(self.src_dir, 'a.pyx')
+ a_c = a_pyx[:-4] + '.c'
+
+ with open(a_pyx, 'w') as f:
+ f.write(content1)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ self.assertEqual(1, len(self.cache_files('a.c*')))
+ with open(a_c) as f:
+ a_contents1 = f.read()
+ os.unlink(a_c)
+
+ with open(a_pyx, 'w') as f:
+ f.write(content2)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ with open(a_c) as f:
+ a_contents2 = f.read()
+ os.unlink(a_c)
+
+ self.assertNotEqual(a_contents1, a_contents2, 'C file not changed!')
+ self.assertEqual(2, len(self.cache_files('a.c*')))
+
+ with open(a_pyx, 'w') as f:
+ f.write(content1)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ self.assertEqual(2, len(self.cache_files('a.c*')))
+ with open(a_c) as f:
+ a_contents = f.read()
+ self.assertEqual(
+ a_contents, a_contents1,
+ msg='\n'.join(list(difflib.unified_diff(
+ a_contents.split('\n'), a_contents1.split('\n')))[:10]))
+
+ def test_cycache_uses_cache(self):
+ a_pyx = os.path.join(self.src_dir, 'a.pyx')
+ a_c = a_pyx[:-4] + '.c'
+ with open(a_pyx, 'w') as f:
+ f.write('pass')
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ a_cache = os.path.join(self.cache_dir, os.listdir(self.cache_dir)[0])
+ gzip.GzipFile(a_cache, 'wb').write('fake stuff'.encode('ascii'))
+ os.unlink(a_c)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ with open(a_c) as f:
+ a_contents = f.read()
+ self.assertEqual(a_contents, 'fake stuff',
+ 'Unexpected contents: %s...' % a_contents[:100])
+
+ def test_multi_file_output(self):
+ a_pyx = os.path.join(self.src_dir, 'a.pyx')
+ a_c = a_pyx[:-4] + '.c'
+ a_h = a_pyx[:-4] + '.h'
+ a_api_h = a_pyx[:-4] + '_api.h'
+ with open(a_pyx, 'w') as f:
+ f.write('cdef public api int foo(int x): return x\n')
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ expected = [a_c, a_h, a_api_h]
+ for output in expected:
+ self.assertTrue(os.path.exists(output), output)
+ os.unlink(output)
+ self.fresh_cythonize(a_pyx, cache=self.cache_dir)
+ for output in expected:
+ self.assertTrue(os.path.exists(output), output)
+
+ def test_options_invalidation(self):
+ hash_pyx = os.path.join(self.src_dir, 'options.pyx')
+ hash_c = hash_pyx[:-len('.pyx')] + '.c'
+
+ with open(hash_pyx, 'w') as f:
+ f.write('pass')
+ self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False)
+ self.assertEqual(1, len(self.cache_files('options.c*')))
+
+ os.unlink(hash_c)
+ self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=True)
+ self.assertEqual(2, len(self.cache_files('options.c*')))
+
+ os.unlink(hash_c)
+ self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=False)
+ self.assertEqual(2, len(self.cache_files('options.c*')))
+
+ os.unlink(hash_c)
+ self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=True)
+ self.assertEqual(2, len(self.cache_files('options.c*')))
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestCythonizeArgsParser.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestCythonizeArgsParser.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestCythonizeArgsParser.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestCythonizeArgsParser.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,482 @@
+from Cython.Build.Cythonize import (
+ create_args_parser, parse_args_raw, parse_args,
+ parallel_compiles
+)
+
+from Cython.Compiler import Options
+from Cython.Compiler.Tests.Utils import backup_Options, restore_Options, check_global_options
+
+from unittest import TestCase
+
+import sys
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO # doesn't accept 'str' in Py2
+
+
+class TestCythonizeArgsParser(TestCase):
+
+ def setUp(self):
+ TestCase.setUp(self)
+ self.parse_args = lambda x, parser=create_args_parser() : parse_args_raw(parser, x)
+
+
+ def are_default(self, options, skip):
+ # empty containers
+ empty_containers = ['directives', 'compile_time_env', 'options', 'excludes']
+ are_none = ['language_level', 'annotate', 'build', 'build_inplace', 'force', 'quiet', 'lenient', 'keep_going', 'no_docstrings']
+ for opt_name in empty_containers:
+ if len(getattr(options, opt_name))!=0 and (opt_name not in skip):
+ self.assertEqual(opt_name,"", msg="For option "+opt_name)
+ return False
+ for opt_name in are_none:
+ if (getattr(options, opt_name) is not None) and (opt_name not in skip):
+ self.assertEqual(opt_name,"", msg="For option "+opt_name)
+ return False
+ if options.parallel!=parallel_compiles and ('parallel' not in skip):
+ return False
+ return True
+
+ # testing directives:
+ def test_directive_short(self):
+ options, args = self.parse_args(['-X', 'cdivision=True'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], True)
+
+ def test_directive_long(self):
+ options, args = self.parse_args(['--directive', 'cdivision=True'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], True)
+
+ def test_directive_multiple(self):
+ options, args = self.parse_args(['-X', 'cdivision=True', '-X', 'c_string_type=bytes'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], True)
+ self.assertEqual(options.directives['c_string_type'], 'bytes')
+
+ def test_directive_multiple_v2(self):
+ options, args = self.parse_args(['-X', 'cdivision=True,c_string_type=bytes'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], True)
+ self.assertEqual(options.directives['c_string_type'], 'bytes')
+
+ def test_directive_value_yes(self):
+ options, args = self.parse_args(['-X', 'cdivision=YeS'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], True)
+
+ def test_directive_value_no(self):
+ options, args = self.parse_args(['-X', 'cdivision=no'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']))
+ self.assertEqual(options.directives['cdivision'], False)
+
+ def test_directive_value_invalid(self):
+ with self.assertRaises(ValueError) as context:
+ options, args = self.parse_args(['-X', 'cdivision=sadfasd'])
+
+ def test_directive_key_invalid(self):
+ with self.assertRaises(ValueError) as context:
+ options, args = self.parse_args(['-X', 'abracadabra'])
+
+ def test_directive_no_value(self):
+ with self.assertRaises(ValueError) as context:
+ options, args = self.parse_args(['-X', 'cdivision'])
+
+ def test_directives_types(self):
+ directives = {
+ 'auto_pickle': True,
+ 'c_string_type': 'bytearray',
+ 'c_string_type': 'bytes',
+ 'c_string_type': 'str',
+ 'c_string_type': 'bytearray',
+ 'c_string_type': 'unicode',
+ 'c_string_encoding' : 'ascii',
+ 'language_level' : 2,
+ 'language_level' : 3,
+ 'language_level' : '3str',
+ 'set_initial_path' : 'my_initial_path',
+ }
+ for key, value in directives.items():
+ cmd = '{key}={value}'.format(key=key, value=str(value))
+ options, args = self.parse_args(['-X', cmd])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['directives']), msg = "Error for option: "+cmd)
+ self.assertEqual(options.directives[key], value, msg = "Error for option: "+cmd)
+
+ def test_directives_wrong(self):
+ directives = {
+ 'auto_pickle': 42, # for bool type
+ 'auto_pickle': 'NONONO', # for bool type
+ 'c_string_type': 'bites',
+ #'c_string_encoding' : 'a',
+ #'language_level' : 4,
+ }
+ for key, value in directives.items():
+ cmd = '{key}={value}'.format(key=key, value=str(value))
+ with self.assertRaises(ValueError, msg = "Error for option: "+cmd) as context:
+ options, args = self.parse_args(['-X', cmd])
+
+ def test_compile_time_env_short(self):
+ options, args = self.parse_args(['-E', 'MYSIZE=10'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['compile_time_env']))
+ self.assertEqual(options.compile_time_env['MYSIZE'], 10)
+
+ def test_compile_time_env_long(self):
+ options, args = self.parse_args(['--compile-time-env', 'MYSIZE=10'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['compile_time_env']))
+ self.assertEqual(options.compile_time_env['MYSIZE'], 10)
+
+ def test_compile_time_env_multiple(self):
+ options, args = self.parse_args(['-E', 'MYSIZE=10', '-E', 'ARRSIZE=11'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['compile_time_env']))
+ self.assertEqual(options.compile_time_env['MYSIZE'], 10)
+ self.assertEqual(options.compile_time_env['ARRSIZE'], 11)
+
+ def test_compile_time_env_multiple_v2(self):
+ options, args = self.parse_args(['-E', 'MYSIZE=10,ARRSIZE=11'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['compile_time_env']))
+ self.assertEqual(options.compile_time_env['MYSIZE'], 10)
+ self.assertEqual(options.compile_time_env['ARRSIZE'], 11)
+
+ #testing options
+ def test_option_short(self):
+ options, args = self.parse_args(['-s', 'docstrings=True'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_long(self):
+ options, args = self.parse_args(['--option', 'docstrings=True'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_multiple(self):
+ options, args = self.parse_args(['-s', 'docstrings=True', '-s', 'buffer_max_dims=8'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+ self.assertEqual(options.options['buffer_max_dims'], True) # really?
+
+ def test_option_multiple_v2(self):
+ options, args = self.parse_args(['-s', 'docstrings=True,buffer_max_dims=8'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+ self.assertEqual(options.options['buffer_max_dims'], True) # really?
+
+ def test_option_value_yes(self):
+ options, args = self.parse_args(['-s', 'docstrings=YeS'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_value_4242(self):
+ options, args = self.parse_args(['-s', 'docstrings=4242'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_value_0(self):
+ options, args = self.parse_args(['-s', 'docstrings=0'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], False)
+
+ def test_option_value_emptystr(self):
+ options, args = self.parse_args(['-s', 'docstrings='])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_value_a_str(self):
+ options, args = self.parse_args(['-s', 'docstrings=BB'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_value_no(self):
+ options, args = self.parse_args(['-s', 'docstrings=nO'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], False)
+
+ def test_option_no_value(self):
+ options, args = self.parse_args(['-s', 'docstrings'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['docstrings'], True)
+
+ def test_option_any_key(self):
+ options, args = self.parse_args(['-s', 'abracadabra'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['options']))
+ self.assertEqual(options.options['abracadabra'], True)
+
+ def test_language_level_2(self):
+ options, args = self.parse_args(['-2'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['language_level']))
+ self.assertEqual(options.language_level, 2)
+
+ def test_language_level_3(self):
+ options, args = self.parse_args(['-3'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['language_level']))
+ self.assertEqual(options.language_level, 3)
+
+ def test_language_level_3str(self):
+ options, args = self.parse_args(['--3str'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['language_level']))
+ self.assertEqual(options.language_level, '3str')
+
+ def test_annotate_short(self):
+ options, args = self.parse_args(['-a'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['annotate']))
+ self.assertEqual(options.annotate, 'default')
+
+ def test_annotate_long(self):
+ options, args = self.parse_args(['--annotate'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['annotate']))
+ self.assertEqual(options.annotate, 'default')
+
+ def test_annotate_fullc(self):
+ options, args = self.parse_args(['--annotate-fullc'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['annotate']))
+ self.assertEqual(options.annotate, 'fullc')
+
+ def test_annotate_and_positional(self):
+ options, args = self.parse_args(['-a', 'foo.pyx'])
+ self.assertEqual(args, ['foo.pyx'])
+ self.assertTrue(self.are_default(options, ['annotate']))
+ self.assertEqual(options.annotate, 'default')
+
+ def test_annotate_and_optional(self):
+ options, args = self.parse_args(['-a', '--3str'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['annotate', 'language_level']))
+ self.assertEqual(options.annotate, 'default')
+ self.assertEqual(options.language_level, '3str')
+
+ def test_exclude_short(self):
+ options, args = self.parse_args(['-x', '*.pyx'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['excludes']))
+ self.assertTrue('*.pyx' in options.excludes)
+
+ def test_exclude_long(self):
+ options, args = self.parse_args(['--exclude', '*.pyx'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['excludes']))
+ self.assertTrue('*.pyx' in options.excludes)
+
+ def test_exclude_multiple(self):
+ options, args = self.parse_args(['--exclude', '*.pyx', '--exclude', '*.py', ])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['excludes']))
+ self.assertEqual(options.excludes, ['*.pyx', '*.py'])
+
+ def test_build_short(self):
+ options, args = self.parse_args(['-b'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['build']))
+ self.assertEqual(options.build, True)
+
+ def test_build_long(self):
+ options, args = self.parse_args(['--build'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['build']))
+ self.assertEqual(options.build, True)
+
+ def test_inplace_short(self):
+ options, args = self.parse_args(['-i'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['build_inplace']))
+ self.assertEqual(options.build_inplace, True)
+
+ def test_inplace_long(self):
+ options, args = self.parse_args(['--inplace'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['build_inplace']))
+ self.assertEqual(options.build_inplace, True)
+
+ def test_parallel_short(self):
+ options, args = self.parse_args(['-j', '42'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['parallel']))
+ self.assertEqual(options.parallel, 42)
+
+ def test_parallel_long(self):
+ options, args = self.parse_args(['--parallel', '42'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['parallel']))
+ self.assertEqual(options.parallel, 42)
+
+ def test_force_short(self):
+ options, args = self.parse_args(['-f'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['force']))
+ self.assertEqual(options.force, True)
+
+ def test_force_long(self):
+ options, args = self.parse_args(['--force'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['force']))
+ self.assertEqual(options.force, True)
+
+ def test_quite_short(self):
+ options, args = self.parse_args(['-q'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['quiet']))
+ self.assertEqual(options.quiet, True)
+
+ def test_quite_long(self):
+ options, args = self.parse_args(['--quiet'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['quiet']))
+ self.assertEqual(options.quiet, True)
+
+ def test_lenient_long(self):
+ options, args = self.parse_args(['--lenient'])
+ self.assertTrue(self.are_default(options, ['lenient']))
+ self.assertFalse(args)
+ self.assertEqual(options.lenient, True)
+
+ def test_keep_going_short(self):
+ options, args = self.parse_args(['-k'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['keep_going']))
+ self.assertEqual(options.keep_going, True)
+
+ def test_keep_going_long(self):
+ options, args = self.parse_args(['--keep-going'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['keep_going']))
+ self.assertEqual(options.keep_going, True)
+
+ def test_no_docstrings_long(self):
+ options, args = self.parse_args(['--no-docstrings'])
+ self.assertFalse(args)
+ self.assertTrue(self.are_default(options, ['no_docstrings']))
+ self.assertEqual(options.no_docstrings, True)
+
+ def test_file_name(self):
+ options, args = self.parse_args(['file1.pyx', 'file2.pyx'])
+ self.assertEqual(len(args), 2)
+ self.assertEqual(args[0], 'file1.pyx')
+ self.assertEqual(args[1], 'file2.pyx')
+ self.assertTrue(self.are_default(options, []))
+
+ def test_option_first(self):
+ options, args = self.parse_args(['-i', 'file.pyx'])
+ self.assertEqual(args, ['file.pyx'])
+ self.assertEqual(options.build_inplace, True)
+ self.assertTrue(self.are_default(options, ['build_inplace']))
+
+ def test_file_inbetween(self):
+ options, args = self.parse_args(['-i', 'file.pyx', '-a'])
+ self.assertEqual(args, ['file.pyx'])
+ self.assertEqual(options.build_inplace, True)
+ self.assertEqual(options.annotate, 'default')
+ self.assertTrue(self.are_default(options, ['build_inplace', 'annotate']))
+
+ def test_option_trailing(self):
+ options, args = self.parse_args(['file.pyx', '-i'])
+ self.assertEqual(args, ['file.pyx'])
+ self.assertEqual(options.build_inplace, True)
+ self.assertTrue(self.are_default(options, ['build_inplace']))
+
+ def test_interspersed_positional(self):
+ options, sources = self.parse_args([
+ 'file1.pyx', '-a',
+ 'file2.pyx'
+ ])
+ self.assertEqual(sources, ['file1.pyx', 'file2.pyx'])
+ self.assertEqual(options.annotate, 'default')
+ self.assertTrue(self.are_default(options, ['annotate']))
+
+ def test_interspersed_positional2(self):
+ options, sources = self.parse_args([
+ 'file1.pyx', '-a',
+ 'file2.pyx', '-a', 'file3.pyx'
+ ])
+ self.assertEqual(sources, ['file1.pyx', 'file2.pyx', 'file3.pyx'])
+ self.assertEqual(options.annotate, 'default')
+ self.assertTrue(self.are_default(options, ['annotate']))
+
+ def test_interspersed_positional3(self):
+ options, sources = self.parse_args([
+ '-f', 'f1', 'f2', '-a',
+ 'f3', 'f4', '-a', 'f5'
+ ])
+ self.assertEqual(sources, ['f1', 'f2', 'f3', 'f4', 'f5'])
+ self.assertEqual(options.annotate, 'default')
+ self.assertEqual(options.force, True)
+ self.assertTrue(self.are_default(options, ['annotate', 'force']))
+
+ def test_wrong_option(self):
+ old_stderr = sys.stderr
+ stderr = sys.stderr = StringIO()
+ try:
+ self.assertRaises(SystemExit, self.parse_args,
+ ['--unknown-option']
+ )
+ finally:
+ sys.stderr = old_stderr
+ self.assertTrue(stderr.getvalue())
+
+
+class TestParseArgs(TestCase):
+ def setUp(self):
+ self._options_backup = backup_Options()
+
+ def tearDown(self):
+ restore_Options(self._options_backup)
+
+ def check_default_global_options(self, white_list=[]):
+ self.assertEqual(check_global_options(self._options_backup, white_list), "")
+
+ def test_build_set_for_inplace(self):
+ options, args = parse_args(['foo.pyx', '-i'])
+ self.assertEqual(options.build, True)
+ self.check_default_global_options()
+
+ def test_lenient(self):
+ options, sources = parse_args(['foo.pyx', '--lenient'])
+ self.assertEqual(sources, ['foo.pyx'])
+ self.assertEqual(Options.error_on_unknown_names, False)
+ self.assertEqual(Options.error_on_uninitialized, False)
+ self.check_default_global_options(['error_on_unknown_names', 'error_on_uninitialized'])
+
+ def test_annotate(self):
+ options, sources = parse_args(['foo.pyx', '--annotate'])
+ self.assertEqual(sources, ['foo.pyx'])
+ self.assertEqual(Options.annotate, 'default')
+ self.check_default_global_options(['annotate'])
+
+ def test_annotate_fullc(self):
+ options, sources = parse_args(['foo.pyx', '--annotate-fullc'])
+ self.assertEqual(sources, ['foo.pyx'])
+ self.assertEqual(Options.annotate, 'fullc')
+ self.check_default_global_options(['annotate'])
+
+ def test_no_docstrings(self):
+ options, sources = parse_args(['foo.pyx', '--no-docstrings'])
+ self.assertEqual(sources, ['foo.pyx'])
+ self.assertEqual(Options.docstrings, False)
+ self.check_default_global_options(['docstrings'])
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestInline.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestInline.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestInline.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestInline.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,4 +1,6 @@
-import os, tempfile
+import os
+import tempfile
+import unittest
from Cython.Shadow import inline
from Cython.Build.Inline import safe_type
from Cython.TestUtils import CythonTest
@@ -24,10 +26,10 @@
self.test_kwds['lib_dir'] = lib_dir
def test_simple(self):
- self.assertEquals(inline("return 1+2", **self.test_kwds), 3)
+ self.assertEqual(inline("return 1+2", **self.test_kwds), 3)
def test_types(self):
- self.assertEquals(inline("""
+ self.assertEqual(inline("""
cimport cython
return cython.typeof(a), cython.typeof(b)
""", a=1.0, b=[], **self.test_kwds), ('double', 'list object'))
@@ -35,13 +37,13 @@
def test_locals(self):
a = 1
b = 2
- self.assertEquals(inline("return a+b", **self.test_kwds), 3)
+ self.assertEqual(inline("return a+b", **self.test_kwds), 3)
def test_globals(self):
- self.assertEquals(inline("return global_value + 1", **self.test_kwds), global_value + 1)
+ self.assertEqual(inline("return global_value + 1", **self.test_kwds), global_value + 1)
def test_no_return(self):
- self.assertEquals(inline("""
+ self.assertEqual(inline("""
a = 1
cdef double b = 2
cdef c = []
@@ -49,7 +51,13 @@
def test_def_node(self):
foo = inline("def foo(x): return x * x", **self.test_kwds)['foo']
- self.assertEquals(foo(7), 49)
+ self.assertEqual(foo(7), 49)
+
+ def test_class_ref(self):
+ class Type(object):
+ pass
+ tp = inline("Type")['Type']
+ self.assertEqual(tp, Type)
def test_pure(self):
import cython as cy
@@ -58,13 +66,47 @@
c = cy.declare(cy.pointer(cy.float), &b)
return b
""", a=3, **self.test_kwds)
- self.assertEquals(type(b), float)
-
- if has_numpy:
+ self.assertEqual(type(b), float)
- def test_numpy(self):
- import numpy
- a = numpy.ndarray((10, 20))
- a[0,0] = 10
- self.assertEquals(safe_type(a), 'numpy.ndarray[numpy.float64_t, ndim=2]')
- self.assertEquals(inline("return a[0,0]", a=a, **self.test_kwds), 10.0)
+ def test_compiler_directives(self):
+ self.assertEqual(
+ inline('return sum(x)',
+ x=[1, 2, 3],
+ cython_compiler_directives={'boundscheck': False}),
+ 6
+ )
+
+ def test_lang_version(self):
+ # GH-3419. Caching for inline code didn't always respect compiler directives.
+ inline_divcode = "def f(int a, int b): return a/b"
+ self.assertEqual(
+ inline(inline_divcode, language_level=2)['f'](5,2),
+ 2
+ )
+ self.assertEqual(
+ inline(inline_divcode, language_level=3)['f'](5,2),
+ 2.5
+ )
+ self.assertEqual(
+ inline(inline_divcode, language_level=2)['f'](5,2),
+ 2
+ )
+
+ def test_repeated_use(self):
+ inline_mulcode = "def f(int a, int b): return a * b"
+ self.assertEqual(inline(inline_mulcode)['f'](5, 2), 10)
+ self.assertEqual(inline(inline_mulcode)['f'](5, 3), 15)
+ self.assertEqual(inline(inline_mulcode)['f'](6, 2), 12)
+ self.assertEqual(inline(inline_mulcode)['f'](5, 2), 10)
+
+ f = inline(inline_mulcode)['f']
+ self.assertEqual(f(5, 2), 10)
+ self.assertEqual(f(5, 3), 15)
+
+ @unittest.skipIf(not has_numpy, "NumPy is not available")
+ def test_numpy(self):
+ import numpy
+ a = numpy.ndarray((10, 20))
+ a[0,0] = 10
+ self.assertEqual(safe_type(a), 'numpy.ndarray[numpy.float64_t, ndim=2]')
+ self.assertEqual(inline("return a[0,0]", a=a, **self.test_kwds), 10.0)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestIpythonMagic.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestIpythonMagic.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestIpythonMagic.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestIpythonMagic.py 2022-03-24 10:16:46.000000000 +0000
@@ -3,37 +3,92 @@
"""Tests for the Cython magics extension."""
+from __future__ import absolute_import
+
import os
+import io
import sys
+from contextlib import contextmanager
+from Cython.Build import IpythonMagic
+from Cython.TestUtils import CythonTest
+from Cython.Compiler.Annotate import AnnotationCCodeWriter
try:
- from IPython.testing.globalipapp import get_ipython
- from IPython.utils import py3compat
-except:
- __test__ = False
+ import IPython.testing.globalipapp
+except ImportError:
+ # Disable tests and fake helpers for initialisation below.
+ def skip_if_not_installed(_):
+ return None
+else:
+ def skip_if_not_installed(c):
+ return c
try:
- # disable IPython history thread to avoid having to clean it up
+ # disable IPython history thread before it gets started to avoid having to clean it up
from IPython.core.history import HistoryManager
HistoryManager.enabled = False
except ImportError:
pass
-from Cython.TestUtils import CythonTest
-ip = get_ipython()
-code = py3compat.str_to_unicode("""\
+@contextmanager
+def capture_output():
+ backup = sys.stdout, sys.stderr
+ try:
+ replacement = [
+ io.TextIOWrapper(io.BytesIO(), encoding=sys.stdout.encoding),
+ io.TextIOWrapper(io.BytesIO(), encoding=sys.stderr.encoding),
+ ]
+ sys.stdout, sys.stderr = replacement
+ output = []
+ yield output
+ finally:
+ sys.stdout, sys.stderr = backup
+ for wrapper in replacement:
+ wrapper.seek(0) # rewind
+ output.append(wrapper.read())
+ wrapper.close()
+
+
+code = u"""\
def f(x):
return 2*x
-""")
+"""
-cython3_code = py3compat.str_to_unicode("""\
+cython3_code = u"""\
def f(int x):
return 2 / x
def call(x):
return f(*(x,))
-""")
+"""
+
+pgo_cython3_code = cython3_code + u"""\
+def main():
+ for _ in range(100): call(5)
+main()
+"""
+
+compile_error_code = u'''\
+cdef extern from *:
+ """
+ xxx a=1;
+ """
+ int a;
+def doit():
+ return a
+'''
+
+compile_warning_code = u'''\
+cdef extern from *:
+ """
+ #pragma message ( "CWarning" )
+ int a = 42;
+ """
+ int a;
+def doit():
+ return a
+'''
if sys.platform == 'win32':
@@ -42,26 +97,40 @@
from unittest import skip as skip_win32
except ImportError:
# poor dev's silent @unittest.skip()
- def skip_win32(f):
- return lambda self: None
+ def skip_win32(dummy):
+ def _skip_win32(func):
+ return None
+ return _skip_win32
else:
- def skip_win32(f):
- return f
+ def skip_win32(dummy):
+ def _skip_win32(func):
+ def wrapper(*args, **kwargs):
+ func(*args, **kwargs)
+ return wrapper
+ return _skip_win32
+@skip_if_not_installed
class TestIPythonMagic(CythonTest):
+ @classmethod
+ def setUpClass(cls):
+ CythonTest.setUpClass()
+ cls._ip = IPython.testing.globalipapp.get_ipython()
+
def setUp(self):
CythonTest.setUp(self)
- ip.extension_manager.load_extension('cython')
+ self._ip.extension_manager.load_extension('cython')
def test_cython_inline(self):
+ ip = self._ip
ip.ex('a=10; b=20')
result = ip.run_cell_magic('cython_inline', '', 'return a+b')
self.assertEqual(result, 30)
- @skip_win32
+ @skip_win32('Skip on Windows')
def test_cython_pyximport(self):
+ ip = self._ip
module_name = '_test_cython_pyximport'
ip.run_cell_magic('cython_pyximport', module_name, code)
ip.ex('g = f(10)')
@@ -75,12 +144,14 @@
pass
def test_cython(self):
+ ip = self._ip
ip.run_cell_magic('cython', '', code)
ip.ex('g = f(10)')
self.assertEqual(ip.user_ns['g'], 20.0)
def test_cython_name(self):
# The Cython module named 'mymodule' defines the function f.
+ ip = self._ip
ip.run_cell_magic('cython', '--name=mymodule', code)
# This module can now be imported in the interactive namespace.
ip.ex('import mymodule; g = mymodule.f(10)')
@@ -88,6 +159,7 @@
def test_cython_language_level(self):
# The Cython cell defines the functions f() and call().
+ ip = self._ip
ip.run_cell_magic('cython', '', cython3_code)
ip.ex('g = f(10); h = call(10)')
if sys.version_info[0] < 3:
@@ -99,6 +171,7 @@
def test_cython3(self):
# The Cython cell defines the functions f() and call().
+ ip = self._ip
ip.run_cell_magic('cython', '-3', cython3_code)
ip.ex('g = f(10); h = call(10)')
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0)
@@ -106,17 +179,129 @@
def test_cython2(self):
# The Cython cell defines the functions f() and call().
+ ip = self._ip
ip.run_cell_magic('cython', '-2', cython3_code)
ip.ex('g = f(10); h = call(10)')
self.assertEqual(ip.user_ns['g'], 2 // 10)
self.assertEqual(ip.user_ns['h'], 2 // 10)
- @skip_win32
+ def test_cython_compile_error_shown(self):
+ ip = self._ip
+ with capture_output() as out:
+ ip.run_cell_magic('cython', '-3', compile_error_code)
+ captured_out, captured_err = out
+
+ # it could be that c-level output is captured by distutil-extension
+ # (and not by us) and is printed to stdout:
+ captured_all = captured_out + "\n" + captured_err
+ self.assertTrue("error" in captured_all, msg="error in " + captured_all)
+
+ def test_cython_link_error_shown(self):
+ ip = self._ip
+ with capture_output() as out:
+ ip.run_cell_magic('cython', '-3 -l=xxxxxxxx', code)
+ captured_out, captured_err = out
+
+ # it could be that c-level output is captured by distutil-extension
+ # (and not by us) and is printed to stdout:
+ captured_all = captured_out + "\n!" + captured_err
+ self.assertTrue("error" in captured_all, msg="error in " + captured_all)
+
+ def test_cython_warning_shown(self):
+ ip = self._ip
+ with capture_output() as out:
+ # force rebuild, otherwise no warning as after the first success
+ # no build step is performed
+ ip.run_cell_magic('cython', '-3 -f', compile_warning_code)
+ captured_out, captured_err = out
+
+ # check that warning was printed to stdout even if build hasn't failed
+ self.assertTrue("CWarning" in captured_out)
+
+ @skip_win32('Skip on Windows')
+ def test_cython3_pgo(self):
+ # The Cython cell defines the functions f() and call().
+ ip = self._ip
+ ip.run_cell_magic('cython', '-3 --pgo', pgo_cython3_code)
+ ip.ex('g = f(10); h = call(10); main()')
+ self.assertEqual(ip.user_ns['g'], 2.0 / 10.0)
+ self.assertEqual(ip.user_ns['h'], 2.0 / 10.0)
+
+ @skip_win32('Skip on Windows')
def test_extlibs(self):
- code = py3compat.str_to_unicode("""
+ ip = self._ip
+ code = u"""
from libc.math cimport sin
x = sin(0.0)
- """)
+ """
ip.user_ns['x'] = 1
ip.run_cell_magic('cython', '-l m', code)
self.assertEqual(ip.user_ns['x'], 0)
+
+
+ def test_cython_verbose(self):
+ ip = self._ip
+ ip.run_cell_magic('cython', '--verbose', code)
+ ip.ex('g = f(10)')
+ self.assertEqual(ip.user_ns['g'], 20.0)
+
+ def test_cython_verbose_thresholds(self):
+ @contextmanager
+ def mock_distutils():
+ class MockLog:
+ DEBUG = 1
+ INFO = 2
+ thresholds = [INFO]
+
+ def set_threshold(self, val):
+ self.thresholds.append(val)
+ return self.thresholds[-2]
+
+
+ new_log = MockLog()
+ old_log = IpythonMagic.distutils.log
+ try:
+ IpythonMagic.distutils.log = new_log
+ yield new_log
+ finally:
+ IpythonMagic.distutils.log = old_log
+
+ ip = self._ip
+ with mock_distutils() as verbose_log:
+ ip.run_cell_magic('cython', '--verbose', code)
+ ip.ex('g = f(10)')
+ self.assertEqual(ip.user_ns['g'], 20.0)
+ self.assertEqual([verbose_log.INFO, verbose_log.DEBUG, verbose_log.INFO],
+ verbose_log.thresholds)
+
+ with mock_distutils() as normal_log:
+ ip.run_cell_magic('cython', '', code)
+ ip.ex('g = f(10)')
+ self.assertEqual(ip.user_ns['g'], 20.0)
+ self.assertEqual([normal_log.INFO], normal_log.thresholds)
+
+ def test_cython_no_annotate(self):
+ ip = self._ip
+ html = ip.run_cell_magic('cython', '', code)
+ self.assertTrue(html is None)
+
+ def test_cython_annotate(self):
+ ip = self._ip
+ html = ip.run_cell_magic('cython', '--annotate', code)
+ # somewhat brittle way to differentiate between annotated htmls
+ # with/without complete source code:
+ self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE not in html.data)
+
+ def test_cython_annotate_default(self):
+ ip = self._ip
+ html = ip.run_cell_magic('cython', '-a', code)
+ # somewhat brittle way to differentiate between annotated htmls
+ # with/without complete source code:
+ self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE not in html.data)
+
+ def test_cython_annotate_complete_c_code(self):
+ ip = self._ip
+ html = ip.run_cell_magic('cython', '--annotate-fullc', code)
+ # somewhat brittle way to differentiate between annotated htmls
+ # with/without complete source code:
+ self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE in html.data)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestRecythonize.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestRecythonize.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestRecythonize.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestRecythonize.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,212 @@
+import shutil
+import os
+import tempfile
+import time
+
+import Cython.Build.Dependencies
+import Cython.Utils
+from Cython.TestUtils import CythonTest
+
+
+def fresh_cythonize(*args, **kwargs):
+ Cython.Utils.clear_function_caches()
+ Cython.Build.Dependencies._dep_tree = None # discard method caches
+ Cython.Build.Dependencies.cythonize(*args, **kwargs)
+
+class TestRecythonize(CythonTest):
+
+ def setUp(self):
+ CythonTest.setUp(self)
+ self.temp_dir = (
+ tempfile.mkdtemp(
+ prefix='recythonize-test',
+ dir='TEST_TMP' if os.path.isdir('TEST_TMP') else None
+ )
+ )
+
+ def tearDown(self):
+ CythonTest.tearDown(self)
+ shutil.rmtree(self.temp_dir)
+
+ def test_recythonize_pyx_on_pxd_change(self):
+
+ src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
+
+ a_pxd = os.path.join(src_dir, 'a.pxd')
+ a_pyx = os.path.join(src_dir, 'a.pyx')
+ a_c = os.path.join(src_dir, 'a.c')
+ dep_tree = Cython.Build.Dependencies.create_dependency_tree()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef int value\n')
+
+ with open(a_pyx, 'w') as f:
+ f.write('value = 1\n')
+
+
+ # The dependencies for "a.pyx" are "a.pxd" and "a.pyx".
+ self.assertEqual({a_pxd, a_pyx}, dep_tree.all_dependencies(a_pyx))
+
+ # Cythonize to create a.c
+ fresh_cythonize(a_pyx)
+
+ # Sleep to address coarse time-stamp precision.
+ time.sleep(1)
+
+ with open(a_c) as f:
+ a_c_contents1 = f.read()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef double value\n')
+
+ fresh_cythonize(a_pyx)
+
+ with open(a_c) as f:
+ a_c_contents2 = f.read()
+
+ self.assertTrue("__pyx_v_1a_value = 1;" in a_c_contents1)
+ self.assertFalse("__pyx_v_1a_value = 1;" in a_c_contents2)
+ self.assertTrue("__pyx_v_1a_value = 1.0;" in a_c_contents2)
+ self.assertFalse("__pyx_v_1a_value = 1.0;" in a_c_contents1)
+
+
+ def test_recythonize_py_on_pxd_change(self):
+
+ src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
+
+ a_pxd = os.path.join(src_dir, 'a.pxd')
+ a_py = os.path.join(src_dir, 'a.py')
+ a_c = os.path.join(src_dir, 'a.c')
+ dep_tree = Cython.Build.Dependencies.create_dependency_tree()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef int value\n')
+
+ with open(a_py, 'w') as f:
+ f.write('value = 1\n')
+
+
+ # The dependencies for "a.py" are "a.pxd" and "a.py".
+ self.assertEqual({a_pxd, a_py}, dep_tree.all_dependencies(a_py))
+
+ # Cythonize to create a.c
+ fresh_cythonize(a_py)
+
+ # Sleep to address coarse time-stamp precision.
+ time.sleep(1)
+
+ with open(a_c) as f:
+ a_c_contents1 = f.read()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef double value\n')
+
+ fresh_cythonize(a_py)
+
+ with open(a_c) as f:
+ a_c_contents2 = f.read()
+
+
+ self.assertTrue("__pyx_v_1a_value = 1;" in a_c_contents1)
+ self.assertFalse("__pyx_v_1a_value = 1;" in a_c_contents2)
+ self.assertTrue("__pyx_v_1a_value = 1.0;" in a_c_contents2)
+ self.assertFalse("__pyx_v_1a_value = 1.0;" in a_c_contents1)
+
+ def test_recythonize_pyx_on_dep_pxd_change(self):
+ src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
+
+ a_pxd = os.path.join(src_dir, 'a.pxd')
+ a_pyx = os.path.join(src_dir, 'a.pyx')
+ b_pyx = os.path.join(src_dir, 'b.pyx')
+ b_c = os.path.join(src_dir, 'b.c')
+ dep_tree = Cython.Build.Dependencies.create_dependency_tree()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef int value\n')
+
+ with open(a_pyx, 'w') as f:
+ f.write('value = 1\n')
+
+ with open(b_pyx, 'w') as f:
+ f.write('cimport a\n' + 'a.value = 2\n')
+
+
+ # The dependencies for "b.pyx" are "a.pxd" and "b.pyx".
+ self.assertEqual({a_pxd, b_pyx}, dep_tree.all_dependencies(b_pyx))
+
+
+ # Cythonize to create b.c
+ fresh_cythonize([a_pyx, b_pyx])
+
+ # Sleep to address coarse time-stamp precision.
+ time.sleep(1)
+
+ with open(b_c) as f:
+ b_c_contents1 = f.read()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef double value\n')
+
+ fresh_cythonize([a_pyx, b_pyx])
+
+ with open(b_c) as f:
+ b_c_contents2 = f.read()
+
+
+
+ self.assertTrue("__pyx_v_1a_value = 2;" in b_c_contents1)
+ self.assertFalse("__pyx_v_1a_value = 2;" in b_c_contents2)
+ self.assertTrue("__pyx_v_1a_value = 2.0;" in b_c_contents2)
+ self.assertFalse("__pyx_v_1a_value = 2.0;" in b_c_contents1)
+
+
+
+ def test_recythonize_py_on_dep_pxd_change(self):
+
+ src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
+
+ a_pxd = os.path.join(src_dir, 'a.pxd')
+ a_pyx = os.path.join(src_dir, 'a.pyx')
+ b_pxd = os.path.join(src_dir, 'b.pxd')
+ b_py = os.path.join(src_dir, 'b.py')
+ b_c = os.path.join(src_dir, 'b.c')
+ dep_tree = Cython.Build.Dependencies.create_dependency_tree()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef int value\n')
+
+ with open(a_pyx, 'w') as f:
+ f.write('value = 1\n')
+
+ with open(b_pxd, 'w') as f:
+ f.write('cimport a\n')
+
+ with open(b_py, 'w') as f:
+ f.write('a.value = 2\n')
+
+
+ # The dependencies for b.py are "a.pxd", "b.pxd" and "b.py".
+ self.assertEqual({a_pxd, b_pxd, b_py}, dep_tree.all_dependencies(b_py))
+
+
+ # Cythonize to create b.c
+ fresh_cythonize([a_pyx, b_py])
+
+ # Sleep to address coarse time-stamp precision.
+ time.sleep(1)
+
+ with open(b_c) as f:
+ b_c_contents1 = f.read()
+
+ with open(a_pxd, 'w') as f:
+ f.write('cdef double value\n')
+
+ fresh_cythonize([a_pyx, b_py])
+
+ with open(b_c) as f:
+ b_c_contents2 = f.read()
+
+ self.assertTrue("__pyx_v_1a_value = 2;" in b_c_contents1)
+ self.assertFalse("__pyx_v_1a_value = 2;" in b_c_contents2)
+ self.assertTrue("__pyx_v_1a_value = 2.0;" in b_c_contents2)
+ self.assertFalse("__pyx_v_1a_value = 2.0;" in b_c_contents1)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestStripLiterals.py cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestStripLiterals.py
--- cython-0.20.1+1~201611251650-6686/Cython/Build/Tests/TestStripLiterals.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Build/Tests/TestStripLiterals.py 2022-03-24 10:16:46.000000000 +0000
@@ -6,10 +6,10 @@
def t(self, before, expected):
actual, literals = strip_string_literals(before, prefix="_L")
- self.assertEquals(expected, actual)
+ self.assertEqual(expected, actual)
for key, value in literals.items():
actual = actual.replace(key, value)
- self.assertEquals(before, actual)
+ self.assertEqual(before, actual)
def test_empty(self):
self.t("", "")
@@ -54,4 +54,3 @@
def test_extern(self):
self.t("cdef extern from 'a.h': # comment",
"cdef extern from '_L1_': #_L2_")
-
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/CodeWriter.py cython-0.20.1+1~202203241016-9537/Cython/CodeWriter.py
--- cython-0.20.1+1~201611251650-6686/Cython/CodeWriter.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/CodeWriter.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,7 +1,6 @@
"""
Serializes a Cython code tree to Cython code. This is primarily useful for
debugging and testing purposes.
-
The output is in a strict format, no whitespace or comments from the input
is preserved (and it could not be as it is not present in the code tree).
"""
@@ -10,6 +9,7 @@
from .Compiler.Visitor import TreeVisitor
from .Compiler.ExprNodes import *
+from .Compiler.Nodes import CSimpleBaseTypeNode
class LinesResult(object):
@@ -28,7 +28,11 @@
self.put(s)
self.newline()
+
class DeclarationWriter(TreeVisitor):
+ """
+ A Cython code writer that is limited to declarations nodes.
+ """
indent_string = u" "
@@ -76,6 +80,14 @@
self.visit(item.default)
self.put(u", ")
self.visit(items[-1])
+ if output_rhs and items[-1].default is not None:
+ self.put(u" = ")
+ self.visit(items[-1].default)
+
+ def _visit_indented(self, node):
+ self.indent()
+ self.visit(node)
+ self.dedent()
def visit_Node(self, node):
raise AssertionError("Node not handled by serializer: %r" % node)
@@ -85,16 +97,14 @@
def visit_StatListNode(self, node):
self.visitchildren(node)
-
+
def visit_CDefExternNode(self, node):
if node.include_file is None:
file = u'*'
else:
file = u'"%s"' % node.include_file
self.putline(u"cdef extern from %s:" % file)
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
def visit_CPtrDeclaratorNode(self, node):
self.put('*')
@@ -111,13 +121,6 @@
self.visit(node.dimension)
self.put(u']')
- def visit_CArrayDeclaratorNode(self, node):
- self.visit(node.base)
- self.put(u'[')
- if node.dimension is not None:
- self.visit(node.dimension)
- self.put(u']')
-
def visit_CFuncDeclaratorNode(self, node):
# TODO: except, gil, etc.
self.visit(node.base)
@@ -136,13 +139,12 @@
self.put("short " * -node.longness)
elif node.longness > 0:
self.put("long " * node.longness)
- self.put(node.name)
+ if node.name is not None:
+ self.put(node.name)
def visit_CComplexBaseTypeNode(self, node):
- self.put(u'(')
self.visit(node.base_type)
self.visit(node.declarator)
- self.put(u')')
def visit_CNestedBaseTypeNode(self, node):
self.visit(node.base_type)
@@ -162,7 +164,7 @@
self.comma_separated_list(node.declarators, output_rhs=True)
self.endline()
- def visit_container_node(self, node, decl, extras, attributes):
+ def _visit_container_node(self, node, decl, extras, attributes):
# TODO: visibility
self.startline(decl)
if node.name:
@@ -191,7 +193,7 @@
if node.packed:
decl += u'packed '
decl += node.kind
- self.visit_container_node(node, decl, None, node.attributes)
+ self._visit_container_node(node, decl, None, node.attributes)
def visit_CppClassNode(self, node):
extras = ""
@@ -199,10 +201,10 @@
extras = u"[%s]" % ", ".join(node.templates)
if node.base_classes:
extras += "(%s)" % ", ".join(node.base_classes)
- self.visit_container_node(node, u"cdef cppclass", extras, node.attributes)
+ self._visit_container_node(node, u"cdef cppclass", extras, node.attributes)
def visit_CEnumDefNode(self, node):
- self.visit_container_node(node, u"cdef enum", None, node.items)
+ self._visit_container_node(node, u"cdef enum", None, node.items)
def visit_CEnumDefItemNode(self, node):
self.startline(node.name)
@@ -228,9 +230,7 @@
self.put(node.base_class_name)
self.put(u")")
self.endline(u":")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
def visit_CTypeDefNode(self, node):
self.startline(u"ctypedef ")
@@ -240,17 +240,49 @@
self.endline()
def visit_FuncDefNode(self, node):
+ # TODO: support cdef + cpdef functions
self.startline(u"def %s(" % node.name)
self.comma_separated_list(node.args)
self.endline(u"):")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
+
+ def visit_CFuncDefNode(self, node):
+ self.startline(u'cpdef ' if node.overridable else u'cdef ')
+ if node.modifiers:
+ self.put(' '.join(node.modifiers))
+ self.put(' ')
+ if node.visibility != 'private':
+ self.put(node.visibility)
+ self.put(u' ')
+ if node.api:
+ self.put(u'api ')
+
+ if node.base_type:
+ self.visit(node.base_type)
+ if node.base_type.name is not None:
+ self.put(u' ')
+
+ # visit the CFuncDeclaratorNode, but put a `:` at the end of line
+ self.visit(node.declarator.base)
+ self.put(u'(')
+ self.comma_separated_list(node.declarator.args)
+ self.endline(u'):')
+
+ self._visit_indented(node.body)
def visit_CArgDeclNode(self, node):
- if node.base_type.name is not None:
+ # For "CSimpleBaseTypeNode", the variable type may have been parsed as type.
+ # For other node types, the "name" is always None.
+ if not isinstance(node.base_type, CSimpleBaseTypeNode) or \
+ node.base_type.name is not None:
self.visit(node.base_type)
- self.put(u" ")
+
+ # If we printed something for "node.base_type", we may need to print an extra ' '.
+ #
+ # Special case: if "node.declarator" is a "CNameDeclaratorNode",
+ # its "name" might be an empty string, for example, for "cdef f(x)".
+ if node.declarator.declared_name():
+ self.put(u" ")
self.visit(node.declarator)
if node.default is not None:
self.put(u" = ")
@@ -284,46 +316,20 @@
def visit_NameNode(self, node):
self.put(node.name)
- def visit_IntNode(self, node):
- self.put(node.value)
-
- def visit_NoneNode(self, node):
- self.put(u"None")
-
- def visit_NotNode(self, node):
- self.put(u"(not ")
- self.visit(node.operand)
- self.put(u")")
-
def visit_DecoratorNode(self, node):
self.startline("@")
self.visit(node.decorator)
self.endline()
- def visit_BinopNode(self, node):
- self.visit(node.operand1)
- self.put(u" %s " % node.operator)
- self.visit(node.operand2)
-
- def visit_AttributeNode(self, node):
- self.visit(node.obj)
- self.put(u".%s" % node.attribute)
-
- def visit_BoolNode(self, node):
- self.put(str(node.value))
-
- # FIXME: represent string nodes correctly
- def visit_StringNode(self, node):
- value = node.value
- if value.encoding is not None:
- value = value.encode(value.encoding)
- self.put(repr(value))
-
def visit_PassStatNode(self, node):
self.startline(u"pass")
self.endline()
-class CodeWriter(DeclarationWriter):
+
+class StatementWriter(DeclarationWriter):
+ """
+ A Cython code writer for most language statement features.
+ """
def visit_SingleAssignmentNode(self, node):
self.startline()
@@ -349,69 +355,51 @@
def visit_ForInStatNode(self, node):
self.startline(u"for ")
- self.visit(node.target)
+ if node.target.is_sequence_constructor:
+ self.comma_separated_list(node.target.args)
+ else:
+ self.visit(node.target)
self.put(u" in ")
self.visit(node.iterator.sequence)
self.endline(u":")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
if node.else_clause is not None:
self.line(u"else:")
- self.indent()
- self.visit(node.else_clause)
- self.dedent()
+ self._visit_indented(node.else_clause)
def visit_IfStatNode(self, node):
- # The IfClauseNode is handled directly without a seperate match
+ # The IfClauseNode is handled directly without a separate match
# for clariy.
self.startline(u"if ")
self.visit(node.if_clauses[0].condition)
self.endline(":")
- self.indent()
- self.visit(node.if_clauses[0].body)
- self.dedent()
+ self._visit_indented(node.if_clauses[0].body)
for clause in node.if_clauses[1:]:
self.startline("elif ")
self.visit(clause.condition)
self.endline(":")
- self.indent()
- self.visit(clause.body)
- self.dedent()
+ self._visit_indented(clause.body)
if node.else_clause is not None:
self.line("else:")
- self.indent()
- self.visit(node.else_clause)
- self.dedent()
+ self._visit_indented(node.else_clause)
- def visit_SequenceNode(self, node):
- self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
+ def visit_WhileStatNode(self, node):
+ self.startline(u"while ")
+ self.visit(node.condition)
+ self.endline(u":")
+ self._visit_indented(node.body)
+ if node.else_clause is not None:
+ self.line("else:")
+ self._visit_indented(node.else_clause)
- def visit_SimpleCallNode(self, node):
- self.visit(node.function)
- self.put(u"(")
- self.comma_separated_list(node.args)
- self.put(")")
+ def visit_ContinueStatNode(self, node):
+ self.line(u"continue")
- def visit_GeneralCallNode(self, node):
- self.visit(node.function)
- self.put(u"(")
- posarg = node.positional_args
- if isinstance(posarg, AsTupleNode):
- self.visit(posarg.arg)
- else:
- self.comma_separated_list(posarg.args) # TupleNode.args
- if node.keyword_args:
- if isinstance(node.keyword_args, DictNode):
- for i, (name, value) in enumerate(node.keyword_args.key_value_pairs):
- if i > 0:
- self.put(', ')
- self.visit(name)
- self.put('=')
- self.visit(value)
- else:
- raise Exception("Not implemented yet")
- self.put(u")")
+ def visit_BreakStatNode(self, node):
+ self.line(u"break")
+
+ def visit_SequenceNode(self, node):
+ self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
def visit_ExprStatNode(self, node):
self.startline()
@@ -433,25 +421,17 @@
self.put(u" as ")
self.visit(node.target)
self.endline(u":")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
def visit_TryFinallyStatNode(self, node):
self.line(u"try:")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
self.line(u"finally:")
- self.indent()
- self.visit(node.finally_clause)
- self.dedent()
+ self._visit_indented(node.finally_clause)
def visit_TryExceptStatNode(self, node):
self.line(u"try:")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
for x in node.except_clauses:
self.visit(x)
if node.else_clause is not None:
@@ -466,13 +446,13 @@
self.put(u", ")
self.visit(node.target)
self.endline(":")
- self.indent()
- self.visit(node.body)
- self.dedent()
+ self._visit_indented(node.body)
def visit_ReturnStatNode(self, node):
- self.startline("return ")
- self.visit(node.value)
+ self.startline("return")
+ if node.value is not None:
+ self.put(u" ")
+ self.visit(node.value)
self.endline()
def visit_ReraiseStatNode(self, node):
@@ -498,24 +478,340 @@
self.put(self.tempnames[node.handle])
-class PxdWriter(DeclarationWriter):
+class ExpressionWriter(TreeVisitor):
+ """
+ A Cython code writer that is intentionally limited to expressions.
+ """
+
+ def __init__(self, result=None):
+ super(ExpressionWriter, self).__init__()
+ if result is None:
+ result = u""
+ self.result = result
+ self.precedence = [0]
+
+ def write(self, tree):
+ self.visit(tree)
+ return self.result
+
+ def put(self, s):
+ self.result += s
+
+ def remove(self, s):
+ if self.result.endswith(s):
+ self.result = self.result[:-len(s)]
+
+ def comma_separated_list(self, items):
+ if len(items) > 0:
+ for item in items[:-1]:
+ self.visit(item)
+ self.put(u", ")
+ self.visit(items[-1])
+
+ def visit_Node(self, node):
+ raise AssertionError("Node not handled by serializer: %r" % node)
+
+ def visit_IntNode(self, node):
+ self.put(node.value)
+
+ def visit_FloatNode(self, node):
+ self.put(node.value)
+
+ def visit_NoneNode(self, node):
+ self.put(u"None")
+
+ def visit_NameNode(self, node):
+ self.put(node.name)
+
+ def visit_EllipsisNode(self, node):
+ self.put(u"...")
+
+ def visit_BoolNode(self, node):
+ self.put(str(node.value))
+
+ def visit_ConstNode(self, node):
+ self.put(str(node.value))
+
+ def visit_ImagNode(self, node):
+ self.put(node.value)
+ self.put(u"j")
+
+ def emit_string(self, node, prefix=u""):
+ repr_val = repr(node.value)
+ if repr_val[0] in 'ub':
+ repr_val = repr_val[1:]
+ self.put(u"%s%s" % (prefix, repr_val))
+
+ def visit_BytesNode(self, node):
+ self.emit_string(node, u"b")
+
+ def visit_StringNode(self, node):
+ self.emit_string(node)
+
+ def visit_UnicodeNode(self, node):
+ self.emit_string(node, u"u")
+
+ def emit_sequence(self, node, parens=(u"", u"")):
+ open_paren, close_paren = parens
+ items = node.subexpr_nodes()
+ self.put(open_paren)
+ self.comma_separated_list(items)
+ self.put(close_paren)
+
+ def visit_ListNode(self, node):
+ self.emit_sequence(node, u"[]")
+
+ def visit_TupleNode(self, node):
+ self.emit_sequence(node, u"()")
+
+ def visit_SetNode(self, node):
+ if len(node.subexpr_nodes()) > 0:
+ self.emit_sequence(node, u"{}")
+ else:
+ self.put(u"set()")
+
+ def visit_DictNode(self, node):
+ self.emit_sequence(node, u"{}")
+
+ def visit_DictItemNode(self, node):
+ self.visit(node.key)
+ self.put(u": ")
+ self.visit(node.value)
+
+ unop_precedence = {
+ 'not': 3, '!': 3,
+ '+': 11, '-': 11, '~': 11,
+ }
+ binop_precedence = {
+ 'or': 1,
+ 'and': 2,
+ # unary: 'not': 3, '!': 3,
+ 'in': 4, 'not_in': 4, 'is': 4, 'is_not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4,
+ '|': 5,
+ '^': 6,
+ '&': 7,
+ '<<': 8, '>>': 8,
+ '+': 9, '-': 9,
+ '*': 10, '@': 10, '/': 10, '//': 10, '%': 10,
+ # unary: '+': 11, '-': 11, '~': 11
+ '**': 12,
+ }
+
+ def operator_enter(self, new_prec):
+ old_prec = self.precedence[-1]
+ if old_prec > new_prec:
+ self.put(u"(")
+ self.precedence.append(new_prec)
+
+ def operator_exit(self):
+ old_prec, new_prec = self.precedence[-2:]
+ if old_prec > new_prec:
+ self.put(u")")
+ self.precedence.pop()
+
+ def visit_NotNode(self, node):
+ op = 'not'
+ prec = self.unop_precedence[op]
+ self.operator_enter(prec)
+ self.put(u"not ")
+ self.visit(node.operand)
+ self.operator_exit()
+
+ def visit_UnopNode(self, node):
+ op = node.operator
+ prec = self.unop_precedence[op]
+ self.operator_enter(prec)
+ self.put(u"%s" % node.operator)
+ self.visit(node.operand)
+ self.operator_exit()
+
+ def visit_BinopNode(self, node):
+ op = node.operator
+ prec = self.binop_precedence.get(op, 0)
+ self.operator_enter(prec)
+ self.visit(node.operand1)
+ self.put(u" %s " % op.replace('_', ' '))
+ self.visit(node.operand2)
+ self.operator_exit()
+
+ def visit_BoolBinopNode(self, node):
+ self.visit_BinopNode(node)
+
+ def visit_PrimaryCmpNode(self, node):
+ self.visit_BinopNode(node)
+
+ def visit_IndexNode(self, node):
+ self.visit(node.base)
+ self.put(u"[")
+ if isinstance(node.index, TupleNode):
+ self.emit_sequence(node.index)
+ else:
+ self.visit(node.index)
+ self.put(u"]")
+
+ def visit_SliceIndexNode(self, node):
+ self.visit(node.base)
+ self.put(u"[")
+ if node.start:
+ self.visit(node.start)
+ self.put(u":")
+ if node.stop:
+ self.visit(node.stop)
+ if node.slice:
+ self.put(u":")
+ self.visit(node.slice)
+ self.put(u"]")
+
+ def visit_SliceNode(self, node):
+ if not node.start.is_none:
+ self.visit(node.start)
+ self.put(u":")
+ if not node.stop.is_none:
+ self.visit(node.stop)
+ if not node.step.is_none:
+ self.put(u":")
+ self.visit(node.step)
+
+ def visit_CondExprNode(self, node):
+ self.visit(node.true_val)
+ self.put(u" if ")
+ self.visit(node.test)
+ self.put(u" else ")
+ self.visit(node.false_val)
+
+ def visit_AttributeNode(self, node):
+ self.visit(node.obj)
+ self.put(u".%s" % node.attribute)
+
+ def visit_SimpleCallNode(self, node):
+ self.visit(node.function)
+ self.put(u"(")
+ self.comma_separated_list(node.args)
+ self.put(")")
+
+ def emit_pos_args(self, node):
+ if node is None:
+ return
+ if isinstance(node, AddNode):
+ self.emit_pos_args(node.operand1)
+ self.emit_pos_args(node.operand2)
+ elif isinstance(node, TupleNode):
+ for expr in node.subexpr_nodes():
+ self.visit(expr)
+ self.put(u", ")
+ elif isinstance(node, AsTupleNode):
+ self.put("*")
+ self.visit(node.arg)
+ self.put(u", ")
+ else:
+ self.visit(node)
+ self.put(u", ")
+
+ def emit_kwd_args(self, node):
+ if node is None:
+ return
+ if isinstance(node, MergedDictNode):
+ for expr in node.subexpr_nodes():
+ self.emit_kwd_args(expr)
+ elif isinstance(node, DictNode):
+ for expr in node.subexpr_nodes():
+ self.put(u"%s=" % expr.key.value)
+ self.visit(expr.value)
+ self.put(u", ")
+ else:
+ self.put(u"**")
+ self.visit(node)
+ self.put(u", ")
+
+ def visit_GeneralCallNode(self, node):
+ self.visit(node.function)
+ self.put(u"(")
+ self.emit_pos_args(node.positional_args)
+ self.emit_kwd_args(node.keyword_args)
+ self.remove(u", ")
+ self.put(")")
+
+ def emit_comprehension(self, body, target,
+ sequence, condition,
+ parens=(u"", u"")):
+ open_paren, close_paren = parens
+ self.put(open_paren)
+ self.visit(body)
+ self.put(u" for ")
+ self.visit(target)
+ self.put(u" in ")
+ self.visit(sequence)
+ if condition:
+ self.put(u" if ")
+ self.visit(condition)
+ self.put(close_paren)
+
+ def visit_ComprehensionAppendNode(self, node):
+ self.visit(node.expr)
+
+ def visit_DictComprehensionAppendNode(self, node):
+ self.visit(node.key_expr)
+ self.put(u": ")
+ self.visit(node.value_expr)
+
+ def visit_ComprehensionNode(self, node):
+ tpmap = {'list': u"[]", 'dict': u"{}", 'set': u"{}"}
+ parens = tpmap[node.type.py_type_name()]
+ body = node.loop.body
+ target = node.loop.target
+ sequence = node.loop.iterator.sequence
+ condition = None
+ if hasattr(body, 'if_clauses'):
+ # type(body) is Nodes.IfStatNode
+ condition = body.if_clauses[0].condition
+ body = body.if_clauses[0].body
+ self.emit_comprehension(body, target, sequence, condition, parens)
+
+ def visit_GeneratorExpressionNode(self, node):
+ body = node.loop.body
+ target = node.loop.target
+ sequence = node.loop.iterator.sequence
+ condition = None
+ if hasattr(body, 'if_clauses'):
+ # type(body) is Nodes.IfStatNode
+ condition = body.if_clauses[0].condition
+ body = body.if_clauses[0].body.expr.arg
+ elif hasattr(body, 'expr'):
+ # type(body) is Nodes.ExprStatNode
+ body = body.expr.arg
+ self.emit_comprehension(body, target, sequence, condition, u"()")
+
+
+class PxdWriter(DeclarationWriter, ExpressionWriter):
+ """
+ A Cython code writer for everything supported in pxd files.
+ (currently unused)
+ """
+
def __call__(self, node):
print(u'\n'.join(self.write(node).lines))
return node
def visit_CFuncDefNode(self, node):
- if 'inline' in node.modifiers:
- return
if node.overridable:
self.startline(u'cpdef ')
else:
self.startline(u'cdef ')
+ if node.modifiers:
+ self.put(' '.join(node.modifiers))
+ self.put(' ')
if node.visibility != 'private':
self.put(node.visibility)
self.put(u' ')
if node.api:
self.put(u'api ')
self.visit(node.declarator)
-
+
def visit_StatNode(self, node):
pass
+
+
+class CodeWriter(StatementWriter, ExpressionWriter):
+ """
+ A complete Cython code writer.
+ """
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/AnalysedTreeTransforms.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/AnalysedTreeTransforms.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/AnalysedTreeTransforms.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/AnalysedTreeTransforms.py 2022-03-24 10:16:46.000000000 +0000
@@ -10,9 +10,9 @@
class AutoTestDictTransform(ScopeTrackingTransform):
# Handles autotestdict directive
- blacklist = ['__cinit__', '__dealloc__', '__richcmp__',
- '__nonzero__', '__bool__',
- '__len__', '__contains__']
+ excludelist = ['__cinit__', '__dealloc__', '__richcmp__',
+ '__nonzero__', '__bool__',
+ '__len__', '__contains__']
def visit_ModuleNode(self, node):
if node.is_pxd:
@@ -81,7 +81,7 @@
name = node.entry.name
else:
name = node.name
- if self.scope_type == 'cclass' and name in self.blacklist:
+ if self.scope_type == 'cclass' and name in self.excludelist:
return node
if self.scope_type == 'pyclass':
class_name = self.scope_node.name
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Annotate.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Annotate.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Annotate.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Annotate.py 2022-03-24 10:16:46.000000000 +0000
@@ -23,8 +23,12 @@
class AnnotationCCodeWriter(CCodeWriter):
- def __init__(self, create_from=None, buffer=None, copy_formatting=True):
+ # also used as marker for detection of complete code emission in tests
+ COMPLETE_CODE_TITLE = "Complete cythonized code"
+
+ def __init__(self, create_from=None, buffer=None, copy_formatting=True, show_entire_c_code=False, source_desc=None):
CCodeWriter.__init__(self, create_from, buffer, copy_formatting=copy_formatting)
+ self.show_entire_c_code = show_entire_c_code
if create_from is None:
self.annotation_buffer = StringIO()
self.last_annotated_pos = None
@@ -79,19 +83,11 @@
css.append(HtmlFormatter().get_style_defs('.cython'))
return '\n'.join(css)
- _js = """
- function toggleDiv(id) {
- theDiv = id.nextElementSibling
- if (theDiv.style.display != 'block') theDiv.style.display = 'block';
- else theDiv.style.display = 'none';
- }
- """.strip()
-
_css_template = textwrap.dedent("""
body.cython { font-family: courier; font-size: 12; }
.cython.tag { }
- .cython.line { margin: 0em }
+ .cython.line { color: #000000; margin: 0em }
.cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }
.cython.line .run { background-color: #B0FFB0; }
@@ -114,6 +110,14 @@
.cython.code .c_call { color: #0000FF; }
""")
+ # on-click toggle function to show/hide C source code
+ _onclick_attr = ' onclick="{0}"'.format((
+ "(function(s){"
+ " s.display = s.display === 'block' ? 'none' : 'block'"
+ "})(this.nextElementSibling.style)"
+ ).replace(' ', '') # poor dev's JS minification
+ )
+
def save_annotation(self, source_filename, target_filename, coverage_xml=None):
with Utils.open_source_file(source_filename) as f:
code = f.read()
@@ -141,9 +145,6 @@
-
Generated by Cython {watermark} {more_info}
@@ -151,7 +152,7 @@
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
- ''').format(css=self._css(), js=self._js, watermark=Version.watermark,
+ ''').format(css=self._css(), watermark=Version.watermark,
filename=os.path.basename(source_filename) if source_filename else '',
more_info=coverage_info)
]
@@ -201,17 +202,24 @@
for line in coverage_data.iterfind('lines/line')
)
- def _htmlify_code(self, code):
+ def _htmlify_code(self, code, language):
try:
from pygments import highlight
- from pygments.lexers import CythonLexer
+ from pygments.lexers import CythonLexer, CppLexer
from pygments.formatters import HtmlFormatter
except ImportError:
# no Pygments, just escape the code
return html_escape(code)
+ if language == "cython":
+ lexer = CythonLexer(stripnl=False, stripall=False)
+ elif language == "c/cpp":
+ lexer = CppLexer(stripnl=False, stripall=False)
+ else:
+ # unknown language, use fallback
+ return html_escape(code)
html_code = highlight(
- code, CythonLexer(stripnl=False, stripall=False),
+ code, lexer,
HtmlFormatter(nowrap=True))
return html_code
@@ -231,7 +239,7 @@
return u"%s " % (
group_name, match.group(group_name))
- lines = self._htmlify_code(cython_code).splitlines()
+ lines = self._htmlify_code(cython_code, "cython").splitlines()
lineno_width = len(str(len(lines)))
if not covered_lines:
covered_lines = None
@@ -253,7 +261,7 @@
calls['py_macro_api'] + calls['pyx_macro_api'])
if c_code:
- onclick = " onclick='toggleDiv(this)'"
+ onclick = self._onclick_attr
expandsymbol = '+'
else:
onclick = ''
@@ -282,6 +290,19 @@
outlist.append(u"{code} ".format(
score=score, covered=covered, code=c_code))
outlist.append(u"")
+
+ # now the whole c-code if needed:
+ if self.show_entire_c_code:
+ outlist.append(u'
')
+ onclick_title = u"
+ {title} \n"
+ outlist.append(onclick_title.format(
+ onclick=self._onclick_attr,
+ title=AnnotationCCodeWriter.COMPLETE_CODE_TITLE,
+ ))
+ complete_code_as_html = self._htmlify_code(self.buffer.getvalue(), "c/cpp")
+ outlist.append(u"
{code} ".format(code=complete_code_as_html))
+ outlist.append(u"
")
+
return outlist
@@ -294,7 +315,7 @@
br'(?PPy[A-Z][a-z]+_[A-Z][A-Z_]+)|'
br'(?PPy[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]*)'
br')(?=\()|' # look-ahead to exclude subsequent '(' from replacement
- br'(?P(?:(?<=;) *if .* +)?\{__pyx_filename = .*goto __pyx_L\w+;\})'
+ br'(?P(?:(?<=;) *if [^;]* +)?__PYX_ERR\([^)]+\))'
).decode('ascii')).sub
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/AutoDocTransforms.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/AutoDocTransforms.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/AutoDocTransforms.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/AutoDocTransforms.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,91 +1,97 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function
from .Visitor import CythonTransform
from .StringEncoding import EncodedString
from . import Options
-from . import PyrexTypes, ExprNodes
+from . import PyrexTypes
+from ..CodeWriter import ExpressionWriter
+from .Errors import warning
+
+
+class AnnotationWriter(ExpressionWriter):
+ """
+ A Cython code writer for Python expressions in argument/variable annotations.
+ """
+ def __init__(self, description=None):
+ """description is optional. If specified it is used in
+ warning messages for the nodes that don't convert to string properly.
+ If not specified then no messages are generated.
+ """
+ ExpressionWriter.__init__(self)
+ self.description = description
+ self.incomplete = False
+
+ def visit_Node(self, node):
+ self.put(u"??>")
+ self.incomplete = True
+ if self.description:
+ warning(node.pos,
+ "Failed to convert code to string representation in {0}".format(
+ self.description), level=1)
+
+ def visit_LambdaNode(self, node):
+ # XXX Should we do better?
+ self.put("")
+ self.incomplete = True
+ if self.description:
+ warning(node.pos,
+ "Failed to convert lambda to string representation in {0}".format(
+ self.description), level=1)
+
+ def visit_UnicodeNode(self, node):
+ # Discard Unicode prefix in annotations. Any tool looking at them
+ # would probably expect Py3 string semantics.
+ self.emit_string(node, "")
+
+ def visit_AnnotationNode(self, node):
+ self.put(node.string.unicode_value)
+
class EmbedSignature(CythonTransform):
def __init__(self, context):
super(EmbedSignature, self).__init__(context)
- self.denv = None # XXX
self.class_name = None
self.class_node = None
- unop_precedence = 11
- binop_precedence = {
- 'or': 1,
- 'and': 2,
- 'not': 3,
- 'in': 4, 'not in': 4, 'is': 4, 'is not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4,
- '|': 5,
- '^': 6,
- '&': 7,
- '<<': 8, '>>': 8,
- '+': 9, '-': 9,
- '*': 10, '/': 10, '//': 10, '%': 10,
- # unary: '+': 11, '-': 11, '~': 11
- '**': 12}
-
- def _fmt_expr_node(self, node, precedence=0):
- if isinstance(node, ExprNodes.BinopNode) and not node.inplace:
- new_prec = self.binop_precedence.get(node.operator, 0)
- result = '%s %s %s' % (self._fmt_expr_node(node.operand1, new_prec),
- node.operator,
- self._fmt_expr_node(node.operand2, new_prec))
- if precedence > new_prec:
- result = '(%s)' % result
- elif isinstance(node, ExprNodes.UnopNode):
- result = '%s%s' % (node.operator,
- self._fmt_expr_node(node.operand, self.unop_precedence))
- if precedence > self.unop_precedence:
- result = '(%s)' % result
- elif isinstance(node, ExprNodes.AttributeNode):
- result = '%s.%s' % (self._fmt_expr_node(node.obj), node.attribute)
- else:
- result = node.name
+ def _fmt_expr(self, node):
+ writer = ExpressionWriter()
+ result = writer.write(node)
+ # print(type(node).__name__, '-->', result)
return result
- def _fmt_arg_defv(self, arg):
- default_val = arg.default
- if not default_val:
- return None
- if isinstance(default_val, ExprNodes.NullNode):
- return 'NULL'
- try:
- denv = self.denv # XXX
- ctval = default_val.compile_time_value(self.denv)
- repr_val = repr(ctval)
- if isinstance(default_val, ExprNodes.UnicodeNode):
- if repr_val[:1] != 'u':
- return u'u%s' % repr_val
- elif isinstance(default_val, ExprNodes.BytesNode):
- if repr_val[:1] != 'b':
- return u'b%s' % repr_val
- elif isinstance(default_val, ExprNodes.StringNode):
- if repr_val[:1] in 'ub':
- return repr_val[1:]
- return repr_val
- except Exception:
- try:
- return self._fmt_expr_node(default_val)
- except AttributeError:
- return '??>'
+ def _fmt_annotation(self, node):
+ writer = AnnotationWriter()
+ result = writer.write(node)
+ # print(type(node).__name__, '-->', result)
+ return result
def _fmt_arg(self, arg):
if arg.type is PyrexTypes.py_object_type or arg.is_self_arg:
doc = arg.name
else:
doc = arg.type.declaration_code(arg.name, for_display=1)
- if arg.default:
- arg_defv = self._fmt_arg_defv(arg)
- if arg_defv:
- doc = doc + ('=%s' % arg_defv)
+
+ if arg.annotation:
+ annotation = self._fmt_annotation(arg.annotation)
+ doc = doc + (': %s' % annotation)
+ if arg.default:
+ default = self._fmt_expr(arg.default)
+ doc = doc + (' = %s' % default)
+ elif arg.default:
+ default = self._fmt_expr(arg.default)
+ doc = doc + ('=%s' % default)
return doc
+ def _fmt_star_arg(self, arg):
+ arg_doc = arg.name
+ if arg.annotation:
+ annotation = self._fmt_annotation(arg.annotation)
+ arg_doc = arg_doc + (': %s' % annotation)
+ return arg_doc
+
def _fmt_arglist(self, args,
- npargs=0, pargs=None,
+ npoargs=0, npargs=0, pargs=None,
nkargs=0, kargs=None,
hide_self=False):
arglist = []
@@ -94,11 +100,15 @@
arg_doc = self._fmt_arg(arg)
arglist.append(arg_doc)
if pargs:
- arglist.insert(npargs, '*%s' % pargs.name)
+ arg_doc = self._fmt_star_arg(pargs)
+ arglist.insert(npargs + npoargs, '*%s' % arg_doc)
elif nkargs:
- arglist.insert(npargs, '*')
+ arglist.insert(npargs + npoargs, '*')
+ if npoargs:
+ arglist.insert(npoargs, '/')
if kargs:
- arglist.append('**%s' % kargs.name)
+ arg_doc = self._fmt_star_arg(kargs)
+ arglist.append('**%s' % arg_doc)
return arglist
def _fmt_ret_type(self, ret):
@@ -108,21 +118,25 @@
return ret.declaration_code("", for_display=1)
def _fmt_signature(self, cls_name, func_name, args,
- npargs=0, pargs=None,
+ npoargs=0, npargs=0, pargs=None,
nkargs=0, kargs=None,
+ return_expr=None,
return_type=None, hide_self=False):
arglist = self._fmt_arglist(args,
- npargs, pargs,
+ npoargs, npargs, pargs,
nkargs, kargs,
hide_self=hide_self)
arglist_doc = ', '.join(arglist)
func_doc = '%s(%s)' % (func_name, arglist_doc)
if cls_name:
func_doc = '%s.%s' % (cls_name, func_doc)
- if return_type:
+ ret_doc = None
+ if return_expr:
+ ret_doc = self._fmt_annotation(return_expr)
+ elif return_type:
ret_doc = self._fmt_ret_type(return_type)
- if ret_doc:
- func_doc = '%s -> %s' % (func_doc, ret_doc)
+ if ret_doc:
+ func_doc = '%s -> %s' % (func_doc, ret_doc)
return func_doc
def _embed_signature(self, signature, node_doc):
@@ -171,12 +185,14 @@
else:
class_name, func_name = self.class_name, node.name
+ npoargs = getattr(node, 'num_posonly_args', 0)
nkargs = getattr(node, 'num_kwonly_args', 0)
- npargs = len(node.args) - nkargs
+ npargs = len(node.args) - nkargs - npoargs
signature = self._fmt_signature(
class_name, func_name, node.args,
- npargs, node.star_arg,
+ npoargs, npargs, node.star_arg,
nkargs, node.starstar_arg,
+ return_expr=node.return_type_annotation,
return_type=None, hide_self=hide_self)
if signature:
if is_constructor:
@@ -199,7 +215,7 @@
def visit_CFuncDefNode(self, node):
if not self.current_directives['embedsignature']:
return node
- if not node.overridable: # not cpdef FOO(...):
+ if not node.overridable: # not cpdef FOO(...):
return node
signature = self._fmt_signature(
@@ -215,8 +231,9 @@
old_doc = None
new_doc = self._embed_signature(signature, old_doc)
node.entry.doc = EncodedString(new_doc)
- if hasattr(node, 'py_func') and node.py_func is not None:
- node.py_func.entry.doc = EncodedString(new_doc)
+ py_func = getattr(node, 'py_func', None)
+ if py_func is not None:
+ py_func.entry.doc = EncodedString(new_doc)
return node
def visit_PropertyNode(self, node):
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Buffer.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Buffer.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Buffer.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Buffer.py 2022-03-24 10:16:46.000000000 +0000
@@ -12,7 +12,6 @@
from . import Naming
from . import Symtab
-
def dedent(text, reindent=0):
from textwrap import dedent
text = dedent(text)
@@ -37,7 +36,6 @@
if self.buffers_exists:
use_bufstruct_declare_code(node.scope)
use_py2_buffer_functions(node.scope)
- node.scope.use_utility_code(empty_bufstruct_utility)
return result
@@ -87,7 +85,7 @@
aux_var = scope.declare_var(name=None, cname=cname,
type=type, pos=node.pos)
if entry.is_arg:
- aux_var.used = True # otherwise, NameNode will mark whether it is used
+ aux_var.used = True # otherwise, NameNode will mark whether it is used
return aux_var
@@ -113,9 +111,9 @@
#
# Analysis
#
-buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
+buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
-buffer_positional_options_count = 1 # anything beyond this needs keyword argument
+buffer_positional_options_count = 1 # anything beyond this needs keyword argument
ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
ERR_BUF_TOO_MANY = 'Too many buffer options'
@@ -148,12 +146,12 @@
options = {}
for name, (value, pos) in dictargs.items():
- if not name in buffer_options:
+ if name not in buffer_options:
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
options[name] = value
for name, (value, pos) in zip(buffer_options, posargs):
- if not name in buffer_options:
+ if name not in buffer_options:
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
if name in options:
raise CompileError(pos, ERR_BUF_DUP % name)
@@ -161,7 +159,7 @@
# Check that they are all there and copy defaults
for name in buffer_options:
- if not name in options:
+ if name not in options:
try:
options[name] = defaults[name]
except KeyError:
@@ -300,9 +298,10 @@
ln = []
for i in range(buf_entry.type.ndim):
for fldname in fldnames:
- ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % \
- (pybuffernd_struct, i, fldname,
- pybuffernd_struct, fldname, i))
+ ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % (
+ pybuffernd_struct, i, fldname,
+ pybuffernd_struct, fldname, i,
+ ))
code.putln(' '.join(ln))
def put_init_vars(entry, code):
@@ -318,8 +317,8 @@
code.putln("%s.data = NULL;" % pybuffernd_struct)
code.putln("%s.rcbuffer = &%s;" % (pybuffernd_struct, pybuffer_struct))
+
def put_acquire_arg_buffer(entry, code, pos):
- code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux
getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type)
@@ -328,14 +327,16 @@
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
code.putln("}")
- # An exception raised in arg parsing cannot be catched, so no
+ # An exception raised in arg parsing cannot be caught, so no
# need to care about the buffer then.
put_unpack_buffer_aux_into_scope(entry, code)
+
def put_release_buffer_code(code, entry):
code.globalstate.use_utility_code(acquire_utility_code)
code.putln("__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);" % entry.buffer_aux.buflocal_nd_var.cname)
+
def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type):
ndim = buffer_type.ndim
cast = int(buffer_type.cast)
@@ -344,10 +345,12 @@
dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype)
+ code.globalstate.use_utility_code(acquire_utility_code)
return ("__Pyx_GetBufferAndValidate(&%(pybuffernd_struct)s.rcbuffer->pybuffer, "
"(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, "
"%(cast)d, __pyx_stack)" % locals())
+
def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry,
is_initialized, pos, code):
"""
@@ -365,14 +368,13 @@
"""
buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type
- code.globalstate.use_utility_code(acquire_utility_code)
pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
flags = get_flags(buffer_aux, buffer_type)
- code.putln("{") # Set up necesarry stack for getbuffer
+ code.putln("{") # Set up necessary stack for getbuffer
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
- getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
+ getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
if is_initialized:
# Release any existing buffer
@@ -385,18 +387,19 @@
# before raising the exception. A failure of reacquisition
# will cause the reacquisition exception to be reported, one
# can consider working around this later.
- type, value, tb = [code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
- for i in range(3)]
- code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb))
+ exc_temps = tuple(code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
+ for _ in range(3))
+ code.putln('PyErr_Fetch(&%s, &%s, &%s);' % exc_temps)
code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
- code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % (type, value, tb)) # Do not refnanny these!
+ code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % exc_temps) # Do not refnanny these!
code.globalstate.use_utility_code(raise_buffer_fallback_code)
code.putln('__Pyx_RaiseBufferFallbackError();')
code.putln('} else {')
- code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb))
- for t in (type, value, tb):
- code.funcstate.release_temp(t)
+ code.putln('PyErr_Restore(%s, %s, %s);' % exc_temps)
code.putln('}')
+ code.putln('%s = %s = %s = 0;' % exc_temps)
+ for t in exc_temps:
+ code.funcstate.release_temp(t)
code.putln('}')
# Unpack indices
put_unpack_buffer_aux_into_scope(buf_entry, code)
@@ -417,7 +420,7 @@
put_unpack_buffer_aux_into_scope(buf_entry, code)
code.putln('}')
- code.putln("}") # Release stack
+ code.putln("}") # Release stack
def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
@@ -490,15 +493,6 @@
env.use_utility_code(buffer_struct_declare_code)
-def get_empty_bufstruct_code(max_ndim):
- code = dedent("""
- static Py_ssize_t __Pyx_zeros[] = {%s};
- static Py_ssize_t __Pyx_minusones[] = {%s};
- """) % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim))
- return UtilityCode(proto=code)
-
-empty_bufstruct_utility = get_empty_bufstruct_code(Options.buffer_max_dims)
-
def buf_lookup_full_code(proto, defin, name, nd):
"""
Generates a buffer lookup function for the right number
@@ -519,6 +513,7 @@
""") % (i, i, i, i) for i in range(nd)]
) + "\nreturn ptr;\n}")
+
def buf_lookup_strided_code(proto, defin, name, nd):
"""
Generates a buffer lookup function for the right number
@@ -529,6 +524,7 @@
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset))
+
def buf_lookup_c_code(proto, defin, name, nd):
"""
Similar to strided lookup, but can assume that the last dimension
@@ -542,6 +538,7 @@
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)])
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1))
+
def buf_lookup_fortran_code(proto, defin, name, nd):
"""
Like C lookup, but the first index is optimized instead.
@@ -557,6 +554,7 @@
def use_py2_buffer_functions(env):
env.use_utility_code(GetAndReleaseBufferUtilityCode())
+
class GetAndReleaseBufferUtilityCode(object):
# Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
# For >= 2.6 we do double mode -- use the new buffer interface on objects
@@ -620,7 +618,7 @@
def mangle_dtype_name(dtype):
- # Use prefixes to seperate user defined types from builtins
+ # Use prefixes to separate user defined types from builtins
# (consider "typedef float unsigned_int")
if dtype.is_pyobject:
return "object"
@@ -639,7 +637,7 @@
and return the name of the type info struct.
Structs with two floats of the same size are encoded as complex numbers.
- One can seperate between complex numbers declared as struct or with native
+ One can separate between complex numbers declared as struct or with native
encoding by inspecting to see if the fields field of the type is
filled in.
"""
@@ -671,16 +669,26 @@
if dtype.is_simple_buffer_dtype():
structinfo_name = "NULL"
elif dtype.is_struct:
- fields = dtype.scope.var_entries
- # Must pre-call all used types in order not to recurse utility code
- # writing.
+ struct_scope = dtype.scope
+ if dtype.is_cv_qualified:
+ struct_scope = struct_scope.base_type_scope
+ # Must pre-call all used types in order not to recurse during utility code writing.
+ fields = struct_scope.var_entries
assert len(fields) > 0
types = [get_type_information_cname(code, f.type, maxdepth - 1)
for f in fields]
typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
+
+ if dtype.is_cv_qualified:
+ # roughly speaking, remove "const" from struct_type
+ struct_type = dtype.cv_base_type.empty_declaration_code()
+ else:
+ struct_type = dtype.empty_declaration_code()
+
for f, typeinfo in zip(fields, types):
typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' %
- (typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True)
+ (typeinfo, f.name, struct_type, f.cname), safe=True)
+
typecode.putln(' {NULL, NULL, 0}', safe=True)
typecode.putln("};", safe=True)
else:
@@ -724,25 +732,18 @@
else:
return TempitaUtilityCode.load(util_code_name, "Buffer.c", context=context, **kwargs)
-context = dict(max_dims=str(Options.buffer_max_dims))
-buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare",
- context=context)
-
+context = dict(max_dims=Options.buffer_max_dims)
+buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", context=context)
+buffer_formats_declare_code = load_buffer_utility("BufferFormatStructs")
# Utility function to set the right exception
# The caller should immediately goto_error
raise_indexerror_code = load_buffer_utility("BufferIndexError")
raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil")
-
raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError")
-buffer_structs_code = load_buffer_utility(
- "BufferFormatStructs", proto_block='utility_code_proto_before_types')
-acquire_utility_code = load_buffer_utility("BufferFormatCheck",
- context=context,
- requires=[buffer_structs_code])
+
+acquire_utility_code = load_buffer_utility("BufferGetAndValidate", context=context)
+buffer_format_check_code = load_buffer_utility("BufferFormatCheck", context=context)
# See utility code BufferFormatFromTypeInfo
-_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat", context={},
- requires=[buffer_structs_code])
-typeinfo_compare_code = load_buffer_utility("TypeInfoCompare", context={},
- requires=[buffer_structs_code])
+_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat")
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Builtin.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Builtin.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Builtin.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Builtin.py 2022-03-24 10:16:46.000000000 +0000
@@ -4,11 +4,11 @@
from __future__ import absolute_import
-from .Symtab import BuiltinScope, StructOrUnionScope
+from .StringEncoding import EncodedString
+from .Symtab import BuiltinScope, StructOrUnionScope, ModuleScope
from .Code import UtilityCode
from .TypeSlots import Signature
from . import PyrexTypes
-from . import Options
# C-level implementations of builtin types, functions and methods
@@ -21,6 +21,7 @@
globals_utility_code = UtilityCode.load("Globals", "Builtins.c")
builtin_utility_code = {
+ 'StopAsyncIteration': UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c"),
}
@@ -29,17 +30,19 @@
class _BuiltinOverride(object):
def __init__(self, py_name, args, ret_type, cname, py_equiv="*",
utility_code=None, sig=None, func_type=None,
- is_strict_signature=False, builtin_return_type=None):
+ is_strict_signature=False, builtin_return_type=None,
+ nogil=None):
self.py_name, self.cname, self.py_equiv = py_name, cname, py_equiv
self.args, self.ret_type = args, ret_type
self.func_type, self.sig = func_type, sig
self.builtin_return_type = builtin_return_type
self.is_strict_signature = is_strict_signature
self.utility_code = utility_code
+ self.nogil = nogil
def build_func_type(self, sig=None, self_arg=None):
if sig is None:
- sig = Signature(self.args, self.ret_type)
+ sig = Signature(self.args, self.ret_type, nogil=self.nogil)
sig.exception_check = False # not needed for the current builtins
func_type = sig.function_type(self_arg)
if self.is_strict_signature:
@@ -53,7 +56,7 @@
def __init__(self, py_name, cname=None, field_type=None, field_type_name=None):
self.py_name = py_name
self.cname = cname or py_name
- self.field_type_name = field_type_name # can't do the lookup before the type is declared!
+ self.field_type_name = field_type_name # can't do the lookup before the type is declared!
self.field_type = field_type
def declare_in_type(self, self_type):
@@ -91,31 +94,40 @@
builtin_function_table = [
# name, args, return, C API func, py equiv = "*"
BuiltinFunction('abs', "d", "d", "fabs",
- is_strict_signature = True),
+ is_strict_signature=True, nogil=True),
BuiltinFunction('abs', "f", "f", "fabsf",
- is_strict_signature = True),
- BuiltinFunction('abs', None, None, "__Pyx_abs_int",
- utility_code = UtilityCode.load("abs_int", "Builtins.c"),
+ is_strict_signature=True, nogil=True),
+ BuiltinFunction('abs', "i", "i", "abs",
+ is_strict_signature=True, nogil=True),
+ BuiltinFunction('abs', "l", "l", "labs",
+ is_strict_signature=True, nogil=True),
+ BuiltinFunction('abs', None, None, "__Pyx_abs_longlong",
+ utility_code = UtilityCode.load("abs_longlong", "Builtins.c"),
+ func_type = PyrexTypes.CFuncType(
+ PyrexTypes.c_longlong_type, [
+ PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_longlong_type, None)
+ ],
+ is_strict_signature = True, nogil=True)),
+ ] + list(
+ BuiltinFunction('abs', None, None, "/*abs_{0}*/".format(t.specialization_name()),
func_type = PyrexTypes.CFuncType(
- PyrexTypes.c_uint_type, [
- PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_int_type, None)
- ],
- is_strict_signature = True)),
- BuiltinFunction('abs', None, None, "__Pyx_abs_long",
- utility_code = UtilityCode.load("abs_long", "Builtins.c"),
+ t,
+ [PyrexTypes.CFuncTypeArg("arg", t, None)],
+ is_strict_signature = True, nogil=True))
+ for t in (PyrexTypes.c_uint_type, PyrexTypes.c_ulong_type, PyrexTypes.c_ulonglong_type)
+ ) + list(
+ BuiltinFunction('abs', None, None, "__Pyx_c_abs{0}".format(t.funcsuffix),
func_type = PyrexTypes.CFuncType(
- PyrexTypes.c_ulong_type, [
- PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_long_type, None)
+ t.real_type, [
+ PyrexTypes.CFuncTypeArg("arg", t, None)
],
- is_strict_signature = True)),
- BuiltinFunction('abs', None, None, "__Pyx_abs_longlong",
- utility_code = UtilityCode.load("abs_longlong", "Builtins.c"),
- func_type = PyrexTypes.CFuncType(
- PyrexTypes.c_ulonglong_type, [
- PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_longlong_type, None)
- ],
- is_strict_signature = True)),
- BuiltinFunction('abs', "O", "O", "PyNumber_Absolute"),
+ is_strict_signature = True, nogil=True))
+ for t in (PyrexTypes.c_float_complex_type,
+ PyrexTypes.c_double_complex_type,
+ PyrexTypes.c_longdouble_complex_type)
+ ) + [
+ BuiltinFunction('abs', "O", "O", "__Pyx_PyNumber_Absolute",
+ utility_code=UtilityCode.load("py_abs", "Builtins.c")),
#('all', "", "", ""),
#('any', "", "", ""),
#('ascii', "", "", ""),
@@ -143,7 +155,8 @@
utility_code=getattr3_utility_code),
BuiltinFunction('getattr', "OO", "O", "__Pyx_GetAttr",
utility_code=getattr_utility_code),
- BuiltinFunction('hasattr', "OO", "b", "PyObject_HasAttr"),
+ BuiltinFunction('hasattr', "OO", "b", "__Pyx_HasAttr",
+ utility_code = UtilityCode.load("HasAttr", "Builtins.c")),
BuiltinFunction('hash', "O", "h", "PyObject_Hash"),
#('hex', "", "", ""),
#('id', "", "", ""),
@@ -192,13 +205,13 @@
#('raw_input', "", "", ""),
#('reduce', "", "", ""),
BuiltinFunction('reload', "O", "O", "PyImport_ReloadModule"),
- BuiltinFunction('repr', "O", "O", "PyObject_Repr", builtin_return_type='str'),
+ BuiltinFunction('repr', "O", "O", "PyObject_Repr"), # , builtin_return_type='str'), # add in Cython 3.1
#('round', "", "", ""),
BuiltinFunction('setattr', "OOO", "r", "PyObject_SetAttr"),
#('sum', "", "", ""),
#('sorted', "", "", ""),
#('type', "O", "O", "PyObject_Type"),
- #('unichr', "", "", ""),
+ BuiltinFunction('unichr', "l", "O", "PyUnicode_FromOrdinal", builtin_return_type='unicode'),
#('unicode', "", "", ""),
#('vars', "", "", ""),
#('zip', "", "", ""),
@@ -318,25 +331,30 @@
("set", "PySet_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
BuiltinMethod("clear", "T", "r", "PySet_Clear"),
# discard() and remove() have a special treatment for unhashable values
-# BuiltinMethod("discard", "TO", "r", "PySet_Discard"),
- BuiltinMethod("update", "TO", "r", "__Pyx_PySet_Update",
- utility_code=UtilityCode.load_cached("PySet_Update", "Builtins.c")),
+ BuiltinMethod("discard", "TO", "r", "__Pyx_PySet_Discard",
+ utility_code=UtilityCode.load("py_set_discard", "Optimize.c")),
+ BuiltinMethod("remove", "TO", "r", "__Pyx_PySet_Remove",
+ utility_code=UtilityCode.load("py_set_remove", "Optimize.c")),
+ # update is actually variadic (see Github issue #1645)
+# BuiltinMethod("update", "TO", "r", "__Pyx_PySet_Update",
+# utility_code=UtilityCode.load_cached("PySet_Update", "Builtins.c")),
BuiltinMethod("add", "TO", "r", "PySet_Add"),
BuiltinMethod("pop", "T", "O", "PySet_Pop")]),
("frozenset", "PyFrozenSet_Type", []),
("Exception", "((PyTypeObject*)PyExc_Exception)[0]", []),
+ ("StopAsyncIteration", "((PyTypeObject*)__Pyx_PyExc_StopAsyncIteration)[0]", []),
]
-types_that_construct_their_instance = set([
+types_that_construct_their_instance = frozenset({
# some builtin types do not always return an instance of
# themselves - these do:
'type', 'bool', 'long', 'float', 'complex',
'bytes', 'unicode', 'bytearray',
- 'tuple', 'list', 'dict', 'set', 'frozenset'
+ 'tuple', 'list', 'dict', 'set', 'frozenset',
# 'str', # only in Py3.x
# 'file', # only in Py2.x
-])
+})
builtin_structs_table = [
@@ -376,10 +394,14 @@
utility = builtin_utility_code.get(name)
if name == 'frozenset':
objstruct_cname = 'PySetObject'
+ elif name == 'bytearray':
+ objstruct_cname = 'PyByteArrayObject'
elif name == 'bool':
objstruct_cname = None
elif name == 'Exception':
objstruct_cname = "PyBaseExceptionObject"
+ elif name == 'StopAsyncIteration':
+ objstruct_cname = "PyBaseExceptionObject"
else:
objstruct_cname = 'Py%sObject' % name.capitalize()
the_type = builtin_scope.declare_builtin_type(name, cname, utility, objstruct_cname)
@@ -398,6 +420,7 @@
def init_builtins():
+ #Errors.init_thread() # hopefully not needed - we should not emit warnings ourselves
init_builtin_structs()
init_builtin_types()
init_builtin_funcs()
@@ -406,14 +429,9 @@
'__debug__', PyrexTypes.c_const_type(PyrexTypes.c_bint_type),
pos=None, cname='(!Py_OptimizeFlag)', is_cdef=True)
- entry = builtin_scope.declare_var(
- 'StopAsyncIteration', PyrexTypes.py_object_type,
- pos=None, cname='__Pyx_PyExc_StopAsyncIteration')
- entry.utility_code = UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c")
-
global list_type, tuple_type, dict_type, set_type, frozenset_type
global bytes_type, str_type, unicode_type, basestring_type, slice_type
- global float_type, bool_type, type_type, complex_type, bytearray_type
+ global float_type, long_type, bool_type, type_type, complex_type, bytearray_type
type_type = builtin_scope.lookup('type').type
list_type = builtin_scope.lookup('list').type
tuple_type = builtin_scope.lookup('tuple').type
@@ -427,8 +445,62 @@
basestring_type = builtin_scope.lookup('basestring').type
bytearray_type = builtin_scope.lookup('bytearray').type
float_type = builtin_scope.lookup('float').type
+ long_type = builtin_scope.lookup('long').type
bool_type = builtin_scope.lookup('bool').type
complex_type = builtin_scope.lookup('complex').type
init_builtins()
+
+##############################
+# Support for a few standard library modules that Cython understands (currently typing and dataclasses)
+##############################
+_known_module_scopes = {}
+
+def get_known_standard_library_module_scope(module_name):
+ mod = _known_module_scopes.get(module_name)
+ if mod:
+ return mod
+
+ if module_name == "typing":
+ mod = ModuleScope(module_name, None, None)
+ for name, tp in [
+ ('Dict', dict_type),
+ ('List', list_type),
+ ('Tuple', tuple_type),
+ ('Set', set_type),
+ ('FrozenSet', frozenset_type),
+ ]:
+ name = EncodedString(name)
+ if name == "Tuple":
+ indexed_type = PyrexTypes.PythonTupleTypeConstructor(EncodedString("typing."+name), tp)
+ else:
+ indexed_type = PyrexTypes.PythonTypeConstructor(EncodedString("typing."+name), tp)
+ entry = mod.declare_type(name, indexed_type, pos = None)
+
+ for name in ['ClassVar', 'Optional']:
+ indexed_type = PyrexTypes.SpecialPythonTypeConstructor(EncodedString("typing."+name))
+ entry = mod.declare_type(name, indexed_type, pos = None)
+ _known_module_scopes[module_name] = mod
+ elif module_name == "dataclasses":
+ mod = ModuleScope(module_name, None, None)
+ indexed_type = PyrexTypes.SpecialPythonTypeConstructor(EncodedString("dataclasses.InitVar"))
+ entry = mod.declare_type(EncodedString("InitVar"), indexed_type, pos = None)
+ _known_module_scopes[module_name] = mod
+ return mod
+
+
+def get_known_standard_library_entry(qualified_name):
+ name_parts = qualified_name.split(".")
+ module_name = EncodedString(name_parts[0])
+ rest = name_parts[1:]
+
+ if len(rest) > 1: # for now, we don't know how to deal with any nested modules
+ return None
+
+ mod = get_known_standard_library_module_scope(module_name)
+
+ # eventually handle more sophisticated multiple lookups if needed
+ if mod and rest:
+ return mod.lookup_here(rest[0])
+ return None
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/CmdLine.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/CmdLine.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/CmdLine.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/CmdLine.py 2022-03-24 10:16:46.000000000 +0000
@@ -5,202 +5,222 @@
from __future__ import absolute_import
import os
-import sys
+from argparse import ArgumentParser, Action, SUPPRESS
from . import Options
-usage = """\
-Cython (http://cython.org) is a compiler for code written in the
-Cython language. Cython is based on Pyrex by Greg Ewing.
-
-Usage: cython [options] sourcefile.{pyx,py} ...
-
-Options:
- -V, --version Display version number of cython compiler
- -l, --create-listing Write error messages to a listing file
- -I, --include-dir Search for include files in named directory
- (multiple include directories are allowed).
- -o, --output-file Specify name of generated C file
- -t, --timestamps Only compile newer source files
- -f, --force Compile all source files (overrides implied -t)
- -v, --verbose Be verbose, print file names on multiple compilation
- -p, --embed-positions If specified, the positions in Cython files of each
- function definition is embedded in its docstring.
- --cleanup Release interned objects on python exit, for memory debugging.
- Level indicates aggressiveness, default 0 releases nothing.
- -w, --working Sets the working directory for Cython (the directory modules
- are searched from)
- --gdb Output debug information for cygdb
- --gdb-outdir Specify gdb debug information output directory. Implies --gdb.
-
- -D, --no-docstrings Strip docstrings from the compiled module.
- -a, --annotate Produce a colorized HTML version of the source.
- --annotate-coverage Annotate and include coverage information from cov.xml.
- --line-directives Produce #line directives pointing to the .pyx source
- --cplus Output a C++ rather than C file.
- --embed[=] Generate a main() function that embeds the Python interpreter.
- -2 Compile based on Python-2 syntax and code semantics.
- -3 Compile based on Python-3 syntax and code semantics.
- --lenient Change some compile time errors to runtime errors to
- improve Python compatibility
- --capi-reexport-cincludes Add cincluded headers to any auto-generated header files.
- --fast-fail Abort the compilation on the first error
- --warning-errors, -Werror Make all warnings into errors
- --warning-extra, -Wextra Enable extra warnings
- -X, --directive =[, for a name other than main().')
+ parser.add_argument('-2', dest='language_level', action='store_const', const=2,
+ help='Compile based on Python-2 syntax and code semantics.')
+ parser.add_argument('-3', dest='language_level', action='store_const', const=3,
+ help='Compile based on Python-3 syntax and code semantics.')
+ parser.add_argument('--3str', dest='language_level', action='store_const', const='3str',
+ help='Compile based on Python-3 syntax and code semantics without '
+ 'assuming unicode by default for string literals under Python 2.')
+ parser.add_argument("--lenient", action=SetLenientAction, nargs=0,
+ help='Change some compile time errors to runtime errors to '
+ 'improve Python compatibility')
+ parser.add_argument("--capi-reexport-cincludes", dest='capi_reexport_cincludes', action='store_true',
+ help='Add cincluded headers to any auto-generated header files.')
+ parser.add_argument("--fast-fail", dest='fast_fail', action='store_true',
+ help='Abort the compilation on the first error')
+ parser.add_argument("-Werror", "--warning-errors", dest='warning_errors', action='store_true',
+ help='Make all warnings into errors')
+ parser.add_argument("-Wextra", "--warning-extra", action=ActivateAllWarningsAction, nargs=0,
+ help='Enable extra warnings')
+
+ parser.add_argument('-X', '--directive', metavar='NAME=VALUE,...',
+ dest='compiler_directives', type=str,
+ action=ParseDirectivesAction,
+ help='Overrides a compiler directive')
+ parser.add_argument('-E', '--compile-time-env', metavar='NAME=VALUE,...',
+ dest='compile_time_env', type=str,
+ action=ParseCompileTimeEnvAction,
+ help='Provides compile time env like DEF would do.')
+ parser.add_argument('sources', nargs='*', default=[])
+
+ # TODO: add help
+ parser.add_argument("-z", "--pre-import", dest='pre_import', action='store', type=str, help=SUPPRESS)
+ parser.add_argument("--convert-range", dest='convert_range', action='store_true', help=SUPPRESS)
+ parser.add_argument("--no-c-in-traceback", dest='c_line_in_traceback', action='store_false', help=SUPPRESS)
+ parser.add_argument("--cimport-from-pyx", dest='cimport_from_pyx', action='store_true', help=SUPPRESS)
+ parser.add_argument("--old-style-globals", dest='old_style_globals', action='store_true', help=SUPPRESS)
+
+ # debug stuff:
+ from . import DebugFlags
+ for name in vars(DebugFlags):
+ if name.startswith("debug"):
+ option_name = name.replace('_', '-')
+ parser.add_argument("--" + option_name, action='store_true', help=SUPPRESS)
+
+ return parser
+
+
+def parse_command_line_raw(parser, args):
+ # special handling for --embed and --embed=xxxx as they aren't correctly parsed
+ def filter_out_embed_options(args):
+ with_embed, without_embed = [], []
+ for x in args:
+ if x == '--embed' or x.startswith('--embed='):
+ with_embed.append(x)
+ else:
+ without_embed.append(x)
+ return with_embed, without_embed
-def parse_command_line(args):
- from .Main import CompilationOptions, default_options
+ with_embed, args_without_embed = filter_out_embed_options(args)
- pending_arg = []
+ arguments, unknown = parser.parse_known_args(args_without_embed)
- def pop_arg():
- if not args or pending_arg:
- bad_usage()
- if '=' in args[0] and args[0].startswith('--'): # allow "--long-option=xyz"
- name, value = args.pop(0).split('=', 1)
- pending_arg.append(value)
- return name
- return args.pop(0)
-
- def pop_value(default=None):
- if pending_arg:
- return pending_arg.pop()
- elif default is not None:
- return default
- elif not args:
- bad_usage()
- return args.pop(0)
-
- def get_param(option):
- tail = option[2:]
- if tail:
- return tail
+ sources = arguments.sources
+ del arguments.sources
+
+ # unknown can be either debug, embed or input files or really unknown
+ for option in unknown:
+ if option.startswith('-'):
+ parser.error("unknown option " + option)
else:
- return pop_arg()
+ sources.append(option)
- options = CompilationOptions(default_options)
- sources = []
- while args:
- if args[0].startswith("-"):
- option = pop_arg()
- if option in ("-V", "--version"):
- options.show_version = 1
- elif option in ("-l", "--create-listing"):
- options.use_listing_file = 1
- elif option in ("-+", "--cplus"):
- options.cplus = 1
- elif option == "--embed":
- Options.embed = pop_value("main")
- elif option.startswith("-I"):
- options.include_path.append(get_param(option))
- elif option == "--include-dir":
- options.include_path.append(pop_value())
- elif option in ("-w", "--working"):
- options.working_path = pop_value()
- elif option in ("-o", "--output-file"):
- options.output_file = pop_value()
- elif option in ("-t", "--timestamps"):
- options.timestamps = 1
- elif option in ("-f", "--force"):
- options.timestamps = 0
- elif option in ("-v", "--verbose"):
- options.verbose += 1
- elif option in ("-p", "--embed-positions"):
- Options.embed_pos_in_docstring = 1
- elif option in ("-z", "--pre-import"):
- Options.pre_import = pop_value()
- elif option == "--cleanup":
- Options.generate_cleanup_code = int(pop_value())
- elif option in ("-D", "--no-docstrings"):
- Options.docstrings = False
- elif option in ("-a", "--annotate"):
- Options.annotate = True
- elif option == "--annotate-coverage":
- Options.annotate = True
- Options.annotate_coverage_xml = pop_value()
- elif option == "--convert-range":
- Options.convert_range = True
- elif option == "--line-directives":
- options.emit_linenums = True
- elif option == "--no-c-in-traceback":
- options.c_line_in_traceback = False
- elif option == "--gdb":
- options.gdb_debug = True
- options.output_dir = os.curdir
- elif option == "--gdb-outdir":
- options.gdb_debug = True
- options.output_dir = pop_value()
- elif option == "--lenient":
- Options.error_on_unknown_names = False
- Options.error_on_uninitialized = False
- elif option == '-2':
- options.language_level = 2
- elif option == '-3':
- options.language_level = 3
- elif option == "--capi-reexport-cincludes":
- options.capi_reexport_cincludes = True
- elif option == "--fast-fail":
- Options.fast_fail = True
- elif option in ('-Werror', '--warning-errors'):
- Options.warning_errors = True
- elif option in ('-Wextra', '--warning-extra'):
- options.compiler_directives.update(Options.extra_warnings)
- elif option == "--old-style-globals":
- Options.old_style_globals = True
- elif option == "--directive" or option.startswith('-X'):
- if option.startswith('-X') and option[2:].strip():
- x_args = option[2:]
- else:
- x_args = pop_value()
- try:
- options.compiler_directives = Options.parse_directive_list(
- x_args, relaxed_bool=True,
- current_settings=options.compiler_directives)
- except ValueError as e:
- sys.stderr.write("Error in compiler directive: %s\n" % e.args[0])
- sys.exit(1)
- elif option.startswith('--debug'):
- option = option[2:].replace('-', '_')
- from . import DebugFlags
- if option in dir(DebugFlags):
- setattr(DebugFlags, option, True)
- else:
- sys.stderr.write("Unknown debug flag: %s\n" % option)
- bad_usage()
- elif option in ('-h', '--help'):
- sys.stdout.write(usage)
- sys.exit(0)
- else:
- sys.stderr.write("Unknown compiler flag: %s\n" % option)
- sys.exit(1)
+ # embed-stuff must be handled extra:
+ for x in with_embed:
+ if x == '--embed':
+ name = 'main' # default value
else:
- sources.append(pop_arg())
+ name = x[len('--embed='):]
+ setattr(arguments, 'embed', name)
+
+ return arguments, sources
- if pending_arg:
- bad_usage()
+
+def parse_command_line(args):
+ parser = create_cython_argparser()
+ arguments, sources = parse_command_line_raw(parser, args)
+
+ options = Options.CompilationOptions(Options.default_options)
+ for name, value in vars(arguments).items():
+ if name.startswith('debug'):
+ from . import DebugFlags
+ if name in dir(DebugFlags):
+ setattr(DebugFlags, name, value)
+ else:
+ parser.error("Unknown debug flag: %s\n" % name)
+ elif hasattr(Options, name):
+ setattr(Options, name, value)
+ else:
+ setattr(options, name, value)
if options.use_listing_file and len(sources) > 1:
- sys.stderr.write(
- "cython: Only one source file allowed when using -o\n")
- sys.exit(1)
+ parser.error("cython: Only one source file allowed when using -o\n")
if len(sources) == 0 and not options.show_version:
- bad_usage()
+ parser.error("cython: Need at least one source file\n")
if Options.embed and len(sources) > 1:
- sys.stderr.write(
- "cython: Only one source file allowed when using -embed\n")
- sys.exit(1)
+ parser.error("cython: Only one source file allowed when using -embed\n")
return options, sources
-
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/CodeGeneration.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/CodeGeneration.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/CodeGeneration.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/CodeGeneration.py 2022-03-24 10:16:46.000000000 +0000
@@ -12,7 +12,7 @@
The result is a tuple (StatListNode, ModuleScope), i.e.
everything that is needed from the pxd after it is processed.
- A purer approach would be to seperately compile the pxd code,
+ A purer approach would be to separately compile the pxd code,
but the result would have to be slightly more sophisticated
than pure strings (functions + wanted interned strings +
wanted utility code + wanted cached objects) so for now this
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Code.pxd cython-0.20.1+1~202203241016-9537/Cython/Compiler/Code.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Code.pxd 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Code.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -1,21 +1,27 @@
-
-from __future__ import absolute_import
+# cython: language_level=3
cimport cython
+from ..StringIOTree cimport StringIOTree
+
+
+cdef class UtilityCodeBase(object):
+ cpdef format_code(self, code_string, replace_empty_lines=*)
+
+
+cdef class UtilityCode(UtilityCodeBase):
+ cdef public object name
+ cdef public object proto
+ cdef public object impl
+ cdef public object init
+ cdef public object cleanup
+ cdef public object proto_block
+ cdef public object requires
+ cdef public dict _cache
+ cdef public list specialize_list
+ cdef public object file
+
+ cpdef none_or_sub(self, s, context)
-#cdef class UtilityCodeBase(object):
-# cdef public object name
-# cdef public object proto
-# cdef public object impl
-# cdef public object init
-# cdef public object cleanup
-# cdef public object requires
-# cdef public dict _cache
-# cdef public list specialize_list
-# cdef public object proto_block
-# cdef public object file
-#
-# cpdef format_code(self, code_string, replace_empty_lines=*)
cdef class FunctionState:
cdef public set names_taken
@@ -33,6 +39,7 @@
cdef public object return_from_error_cleanup_label # not used in __init__ ?
cdef public object exc_vars
+ cdef public object current_except
cdef public bint in_try_finally
cdef public bint can_trace
cdef public bint gil_owned
@@ -40,6 +47,7 @@
cdef public list temps_allocated
cdef public dict temps_free
cdef public dict temps_used_type
+ cdef public set zombie_temps
cdef public size_t temp_counter
cdef public list collect_temps_stack
@@ -88,7 +96,27 @@
#def funccontext_property(name):
-#class CCodeWriter(object):
+cdef class CCodeWriter(object):
+ cdef readonly StringIOTree buffer
+ cdef readonly list pyclass_stack
+ cdef readonly object globalstate
+ cdef readonly object funcstate
+ cdef object code_config
+ cdef object last_pos
+ cdef object last_marked_pos
+ cdef Py_ssize_t level
+ cdef public Py_ssize_t call_level # debug-only, see Nodes.py
+ cdef bint bol
+
+ cpdef write(self, s)
+ cpdef put(self, code)
+ cpdef put_safe(self, code)
+ cpdef putln(self, code=*, bint safe=*)
+ @cython.final
+ cdef increase_indent(self)
+ @cython.final
+ cdef decrease_indent(self)
+
cdef class PyrexCodeWriter:
cdef public object f
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Code.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Code.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Code.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Code.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,4 +1,5 @@
-# cython: language_level = 2
+# cython: language_level=3str
+# cython: auto_pickle=False
#
# Code output module
#
@@ -6,31 +7,27 @@
from __future__ import absolute_import
import cython
-cython.declare(os=object, re=object, operator=object,
- Naming=object, Options=object, StringEncoding=object,
+cython.declare(os=object, re=object, operator=object, textwrap=object,
+ Template=object, Naming=object, Options=object, StringEncoding=object,
Utils=object, SourceDescriptor=object, StringIOTree=object,
- DebugFlags=object, basestring=object)
+ DebugFlags=object, basestring=object, defaultdict=object,
+ closing=object, partial=object)
+import hashlib
+import operator
import os
import re
-import sys
-import operator
+import shutil
import textwrap
from string import Template
from functools import partial
from contextlib import closing
from collections import defaultdict
-try:
- import hashlib
-except ImportError:
- import md5 as hashlib
-
from . import Naming
from . import Options
from . import DebugFlags
from . import StringEncoding
-from . import Version
from .. import Utils
from .Scanning import SourceDescriptor
from ..StringIOTree import StringIOTree
@@ -40,8 +37,6 @@
except ImportError:
from builtins import str as basestring
-KEYWORDS_MUST_BE_BYTES = sys.version_info < (2, 7)
-
non_portable_builtins_map = {
# builtins that have different names in different Python versions
@@ -52,30 +47,144 @@
'raw_input' : ('PY_MAJOR_VERSION >= 3', 'input'),
}
+ctypedef_builtins_map = {
+ # types of builtins in "ctypedef class" statements which we don't
+ # import either because the names conflict with C types or because
+ # the type simply is not exposed.
+ 'py_int' : '&PyInt_Type',
+ 'py_long' : '&PyLong_Type',
+ 'py_float' : '&PyFloat_Type',
+ 'wrapper_descriptor' : '&PyWrapperDescr_Type',
+}
+
basicsize_builtins_map = {
# builtins whose type has a different tp_basicsize than sizeof(...)
'PyTypeObject': 'PyHeapTypeObject',
}
uncachable_builtins = [
- # builtin names that cannot be cached because they may or may not
- # be available at import time
+ # Global/builtin names that cannot be cached because they may or may not
+ # be available at import time, for various reasons:
+ ## - Py3.7+
+ 'breakpoint', # might deserve an implementation in Cython
+ ## - Py3.4+
+ '__loader__',
+ '__spec__',
+ ## - Py3+
+ 'BlockingIOError',
+ 'BrokenPipeError',
+ 'ChildProcessError',
+ 'ConnectionAbortedError',
+ 'ConnectionError',
+ 'ConnectionRefusedError',
+ 'ConnectionResetError',
+ 'FileExistsError',
+ 'FileNotFoundError',
+ 'InterruptedError',
+ 'IsADirectoryError',
+ 'ModuleNotFoundError',
+ 'NotADirectoryError',
+ 'PermissionError',
+ 'ProcessLookupError',
+ 'RecursionError',
+ 'ResourceWarning',
+ #'StopAsyncIteration', # backported
+ 'TimeoutError',
+ '__build_class__',
+ 'ascii', # might deserve an implementation in Cython
+ #'exec', # implemented in Cython
+ ## - platform specific
'WindowsError',
- '_', # e.g. gettext
+ ## - others
+ '_', # e.g. used by gettext
]
-special_py_methods = set([
+special_py_methods = cython.declare(frozenset, frozenset((
'__cinit__', '__dealloc__', '__richcmp__', '__next__',
'__await__', '__aiter__', '__anext__',
'__getreadbuffer__', '__getwritebuffer__', '__getsegcount__',
- '__getcharbuffer__', '__getbuffer__', '__releasebuffer__'
-])
+ '__getcharbuffer__', '__getbuffer__', '__releasebuffer__',
+)))
modifier_output_mapper = {
'inline': 'CYTHON_INLINE'
}.get
-is_self_assignment = re.compile(r" *(\w+) = (\1);\s*$").match
+
+class IncludeCode(object):
+ """
+ An include file and/or verbatim C code to be included in the
+ generated sources.
+ """
+ # attributes:
+ #
+ # pieces {order: unicode}: pieces of C code to be generated.
+ # For the included file, the key "order" is zero.
+ # For verbatim include code, the "order" is the "order"
+ # attribute of the original IncludeCode where this piece
+ # of C code was first added. This is needed to prevent
+ # duplication if the same include code is found through
+ # multiple cimports.
+ # location int: where to put this include in the C sources, one
+ # of the constants INITIAL, EARLY, LATE
+ # order int: sorting order (automatically set by increasing counter)
+
+ # Constants for location. If the same include occurs with different
+ # locations, the earliest one takes precedense.
+ INITIAL = 0
+ EARLY = 1
+ LATE = 2
+
+ counter = 1 # Counter for "order"
+
+ def __init__(self, include=None, verbatim=None, late=True, initial=False):
+ self.order = self.counter
+ type(self).counter += 1
+ self.pieces = {}
+
+ if include:
+ if include[0] == '<' and include[-1] == '>':
+ self.pieces[0] = u'#include {0}'.format(include)
+ late = False # system include is never late
+ else:
+ self.pieces[0] = u'#include "{0}"'.format(include)
+
+ if verbatim:
+ self.pieces[self.order] = verbatim
+
+ if initial:
+ self.location = self.INITIAL
+ elif late:
+ self.location = self.LATE
+ else:
+ self.location = self.EARLY
+
+ def dict_update(self, d, key):
+ """
+ Insert `self` in dict `d` with key `key`. If that key already
+ exists, update the attributes of the existing value with `self`.
+ """
+ if key in d:
+ other = d[key]
+ other.location = min(self.location, other.location)
+ other.pieces.update(self.pieces)
+ else:
+ d[key] = self
+
+ def sortkey(self):
+ return self.order
+
+ def mainpiece(self):
+ """
+ Return the main piece of C code, corresponding to the include
+ file. If there was no include file, return None.
+ """
+ return self.pieces.get(0)
+
+ def write(self, code):
+ # Write values of self.pieces dict, sorted by the keys
+ for k in sorted(self.pieces):
+ code.putln(self.pieces[k])
def get_utility_dir():
@@ -84,6 +193,28 @@
Cython_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
return os.path.join(Cython_dir, "Utility")
+read_utilities_hook = None
+"""
+Override the hook for reading a utilities file that contains code fragments used
+by the codegen.
+
+The hook functions takes the path of the utilities file, and returns a list
+of strings, one per line.
+
+The default behavior is to open a file relative to get_utility_dir().
+"""
+
+def read_utilities_from_utility_dir(path):
+ """
+ Read all lines of the file at the provided path from a path relative
+ to get_utility_dir().
+ """
+ filename = os.path.join(get_utility_dir(), path)
+ with closing(Utils.open_source_file(filename, encoding='UTF-8')) as f:
+ return f.readlines()
+
+# by default, read utilities from the utility directory.
+read_utilities_hook = read_utilities_from_utility_dir
class UtilityCodeBase(object):
"""
@@ -105,6 +236,15 @@
[definitions]
+ ##### MyUtility #####
+ #@subsitute: tempita
+
+ [requires tempita substitution
+ - context can't be specified here though so only
+ tempita utility that requires no external context
+ will benefit from this tag
+ - only necessary when @required from non-tempita code]
+
for prototypes and implementation respectively. For non-python or
-cython files backslashes should be used instead. 5 to 30 comment
characters may be used on either side.
@@ -115,7 +255,6 @@
"""
is_cython_utility = False
- requires = None
_utility_cache = {}
@classmethod
@@ -124,8 +263,7 @@
return
code = '\n'.join(lines)
- if tags and 'substitute' in tags and tags['substitute'] == set(['naming']):
- del tags['substitute']
+ if tags and 'substitute' in tags and 'naming' in tags['substitute']:
try:
code = Template(code).substitute(vars(Naming))
except (KeyError, ValueError) as e:
@@ -137,22 +275,15 @@
if type == 'proto':
utility[0] = code
- elif type.startswith('proto.'):
- utility[0] = code
- utility[1] = type[6:]
elif type == 'impl':
- utility[2] = code
+ utility[1] = code
else:
- all_tags = utility[3]
- if KEYWORDS_MUST_BE_BYTES:
- type = type.encode('ASCII')
+ all_tags = utility[2]
all_tags[type] = code
if tags:
- all_tags = utility[3]
+ all_tags = utility[2]
for name, values in tags.items():
- if KEYWORDS_MUST_BE_BYTES:
- name = name.encode('ASCII')
all_tags.setdefault(name, set()).update(values)
@classmethod
@@ -161,11 +292,10 @@
if utilities:
return utilities
- filename = os.path.join(get_utility_dir(), path)
_, ext = os.path.splitext(path)
if ext in ('.pyx', '.py', '.pxd', '.pxi'):
comment = '#'
- strip_comments = partial(re.compile(r'^\s*#.*').sub, '')
+ strip_comments = partial(re.compile(r'^\s*#(?!\s*cython\s*:).*').sub, '')
rstrip = StringEncoding._unicode.rstrip
else:
comment = '/'
@@ -175,12 +305,11 @@
(r'^%(C)s{5,30}\s*(?P(?:\w|\.)+)\s*%(C)s{5,30}|'
r'^%(C)s+@(?P\w+)\s*:\s*(?P(?:\w|[.:])+)') %
{'C': comment}).match
- match_type = re.compile('(.+)[.](proto(?:[.]\S+)?|impl|init|cleanup)$').match
+ match_type = re.compile(r'(.+)[.](proto(?:[.]\S+)?|impl|init|cleanup)$').match
- with closing(Utils.open_source_file(filename, encoding='UTF-8')) as f:
- all_lines = f.readlines()
+ all_lines = read_utilities_hook(path)
- utilities = defaultdict(lambda: [None, None, None, {}])
+ utilities = defaultdict(lambda: [None, None, {}])
lines = []
tags = defaultdict(set)
utility = type = None
@@ -220,43 +349,22 @@
return utilities
@classmethod
- def load(cls, util_code_name, from_file=None, **kwargs):
+ def load(cls, util_code_name, from_file, **kwargs):
"""
Load utility code from a file specified by from_file (relative to
- Cython/Utility) and name util_code_name. If from_file is not given,
- load it from the file util_code_name.*. There should be only one
- file matched by this pattern.
+ Cython/Utility) and name util_code_name.
"""
+
if '::' in util_code_name:
from_file, util_code_name = util_code_name.rsplit('::', 1)
- if not from_file:
- utility_dir = get_utility_dir()
- prefix = util_code_name + '.'
- try:
- listing = os.listdir(utility_dir)
- except OSError:
- # XXX the code below assumes as 'zipimport.zipimporter' instance
- # XXX should be easy to generalize, but too lazy right now to write it
- import zipfile
- global __loader__
- loader = __loader__
- archive = loader.archive
- with closing(zipfile.ZipFile(archive)) as fileobj:
- listing = [os.path.basename(name)
- for name in fileobj.namelist()
- if os.path.join(archive, name).startswith(utility_dir)]
- files = [filename for filename in listing
- if filename.startswith(prefix)]
- if not files:
- raise ValueError("No match found for utility code " + util_code_name)
- if len(files) > 1:
- raise ValueError("More than one filename match found for utility code " + util_code_name)
- from_file = files[0]
-
+ assert from_file
utilities = cls.load_utilities_from_file(from_file)
- proto, proto_block, impl, tags = utilities[util_code_name]
+ proto, impl, tags = utilities[util_code_name]
if tags:
+ if "substitute" in tags and "tempita" in tags["substitute"]:
+ if not issubclass(cls, TempitaUtilityCode):
+ return TempitaUtilityCode.load(util_code_name, from_file, **kwargs)
orig_kwargs = kwargs.copy()
for name, values in tags.items():
if name in kwargs:
@@ -270,16 +378,20 @@
# dependencies are rarely unique, so use load_cached() when we can
values = [cls.load_cached(dep, from_file)
for dep in sorted(values)]
+ elif name == 'substitute':
+ # don't want to pass "naming" or "tempita" to the constructor
+ # since these will have been handled
+ values = values - {'naming', 'tempita'}
+ if not values:
+ continue
elif not values:
values = None
elif len(values) == 1:
- values = values[0]
+ values = list(values)[0]
kwargs[name] = values
if proto is not None:
kwargs['proto'] = proto
- if proto_block is not None:
- kwargs['proto_block'] = proto_block
if impl is not None:
kwargs['impl'] = impl
@@ -291,11 +403,11 @@
return cls(**kwargs)
@classmethod
- def load_cached(cls, utility_code_name, from_file=None, __cache={}):
+ def load_cached(cls, utility_code_name, from_file, __cache={}):
"""
Calls .load(), but using a per-type cache based on utility name and file name.
"""
- key = (cls, from_file, utility_code_name)
+ key = (utility_code_name, from_file, cls)
try:
return __cache[key]
except KeyError:
@@ -304,7 +416,7 @@
return code
@classmethod
- def load_as_string(cls, util_code_name, from_file=None, **kwargs):
+ def load_as_string(cls, util_code_name, from_file, **kwargs):
"""
Load a utility code as a string. Returns (proto, implementation)
"""
@@ -324,7 +436,11 @@
return "<%s(%s)>" % (type(self).__name__, self.name)
def get_tree(self, **kwargs):
- pass
+ return None
+
+ def __deepcopy__(self, memodict=None):
+ # No need to deep-copy utility code since it's essentially immutable.
+ return self
class UtilityCode(UtilityCodeBase):
@@ -336,7 +452,7 @@
hashes/equals by instance
proto C prototypes
- impl implemenation code
+ impl implementation code
init code to call on module initialization
requires utility code dependencies
proto_block the place in the resulting file where the prototype should
@@ -384,9 +500,11 @@
def specialize(self, pyrex_type=None, **data):
# Dicts aren't hashable...
+ name = self.name
if pyrex_type is not None:
data['type'] = pyrex_type.empty_declaration_code()
data['type_name'] = pyrex_type.specialization_name()
+ name = "%s[%s]" % (name, data['type_name'])
key = tuple(sorted(data.items()))
try:
return self._cache[key]
@@ -402,7 +520,9 @@
self.none_or_sub(self.init, data),
self.none_or_sub(self.cleanup, data),
requires,
- self.proto_block)
+ self.proto_block,
+ name,
+ )
self.specialize_list.append(s)
return s
@@ -410,22 +530,23 @@
def inject_string_constants(self, impl, output):
"""Replace 'PYIDENT("xyz")' by a constant Python identifier cname.
"""
- if 'PYIDENT(' not in impl:
+ if 'PYIDENT(' not in impl and 'PYUNICODE(' not in impl:
return False, impl
replacements = {}
def externalise(matchobj):
- name = matchobj.group(1)
+ key = matchobj.groups()
try:
- cname = replacements[name]
+ cname = replacements[key]
except KeyError:
- cname = replacements[name] = output.get_interned_identifier(
- StringEncoding.EncodedString(name)).cname
+ str_type, name = key
+ cname = replacements[key] = output.get_py_string_const(
+ StringEncoding.EncodedString(name), identifier=str_type == 'IDENT').cname
return cname
- impl = re.sub(r'PYIDENT\("([^"]+)"\)', externalise, impl)
- assert 'PYIDENT(' not in impl
- return bool(replacements), impl
+ impl = re.sub(r'PY(IDENT|UNICODE)\("([^"]+)"\)', externalise, impl)
+ assert 'PYIDENT(' not in impl and 'PYUNICODE(' not in impl
+ return True, impl
def inject_unbound_methods(self, impl, output):
"""Replace 'UNBOUND_METHOD(type, "name")' by a constant Python identifier cname.
@@ -433,28 +554,22 @@
if 'CALL_UNBOUND_METHOD(' not in impl:
return False, impl
- utility_code = set()
def externalise(matchobj):
- type_cname, method_name, args = matchobj.groups()
- args = [arg.strip() for arg in args[1:].split(',')]
- if len(args) == 1:
- call = '__Pyx_CallUnboundCMethod0'
- utility_code.add("CallUnboundCMethod0")
- elif len(args) == 2:
- call = '__Pyx_CallUnboundCMethod1'
- utility_code.add("CallUnboundCMethod1")
- else:
- assert False, "CALL_UNBOUND_METHOD() requires 1 or 2 call arguments"
-
- cname = output.get_cached_unbound_method(type_cname, method_name, len(args))
- return '%s(&%s, %s)' % (call, cname, ', '.join(args))
-
- impl = re.sub(r'CALL_UNBOUND_METHOD\(([a-zA-Z_]+),\s*"([^"]+)"((?:,\s*[^),]+)+)\)', externalise, impl)
+ type_cname, method_name, obj_cname, args = matchobj.groups()
+ args = [arg.strip() for arg in args[1:].split(',')] if args else []
+ assert len(args) < 3, "CALL_UNBOUND_METHOD() does not support %d call arguments" % len(args)
+ return output.cached_unbound_method_call_code(obj_cname, type_cname, method_name, args)
+
+ impl = re.sub(
+ r'CALL_UNBOUND_METHOD\('
+ r'([a-zA-Z_]+),' # type cname
+ r'\s*"([^"]+)",' # method name
+ r'\s*([^),]+)' # object cname
+ r'((?:,[^),]+)*)' # args*
+ r'\)', externalise, impl)
assert 'CALL_UNBOUND_METHOD(' not in impl
- for helper in sorted(utility_code):
- output.use_utility_code(UtilityCode.load_cached(helper, "ObjectHandling.c"))
- return bool(utility_code), impl
+ return True, impl
def wrap_c_strings(self, impl):
"""Replace CSTRING('''xyz''') by a C compatible string
@@ -563,6 +678,7 @@
available. Useful when you only have 'env' but not 'code'.
"""
__name__ = ''
+ requires = None
def __init__(self, callback):
self.callback = callback
@@ -601,12 +717,14 @@
self.in_try_finally = 0
self.exc_vars = None
+ self.current_except = None
self.can_trace = False
self.gil_owned = True
- self.temps_allocated = [] # of (name, type, manage_ref, static)
- self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
- self.temps_used_type = {} # name -> (type, manage_ref)
+ self.temps_allocated = [] # of (name, type, manage_ref, static)
+ self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
+ self.temps_used_type = {} # name -> (type, manage_ref)
+ self.zombie_temps = set() # temps that must not be reused after release
self.temp_counter = 0
self.closure_temps = None
@@ -621,6 +739,20 @@
self.should_declare_error_indicator = False
self.uses_error_indicator = False
+ # safety checks
+
+ def validate_exit(self):
+ # validate that all allocated temps have been freed
+ if self.temps_allocated:
+ leftovers = self.temps_in_use()
+ if leftovers:
+ msg = "TEMPGUARD: Temps left over at end of '%s': %s" % (self.scope.name, ', '.join([
+ '%s [%s]' % (name, ctype)
+ for name, ctype, is_pytemp in sorted(leftovers)]),
+ )
+ #print(msg)
+ raise RuntimeError(msg)
+
# labels
def new_label(self, name=None):
@@ -631,8 +763,8 @@
label += '_' + name
return label
- def new_yield_label(self):
- label = self.new_label('resume_from_yield')
+ def new_yield_label(self, expr_type='yield'):
+ label = self.new_label('resume_from_%s' % expr_type)
num_and_label = (len(self.yield_labels) + 1, label)
self.yield_labels.append(num_and_label)
return num_and_label
@@ -690,7 +822,7 @@
# temp handling
- def allocate_temp(self, type, manage_ref, static=False):
+ def allocate_temp(self, type, manage_ref, static=False, reusable=True):
"""
Allocates a temporary (which may create a new one or get a previously
allocated and released one of the same type). Type is simply registered
@@ -709,19 +841,26 @@
This is only used when allocating backing store for a module-level
C array literals.
+ if reusable=False, the temp will not be reused after release.
+
A C string referring to the variable is returned.
"""
- if type.is_const and not type.is_reference:
- type = type.const_base_type
+ if type.is_cv_qualified and not type.is_reference:
+ type = type.cv_base_type
elif type.is_reference and not type.is_fake_reference:
type = type.ref_base_type
+ elif type.is_cfunction:
+ from . import PyrexTypes
+ type = PyrexTypes.c_ptr_type(type) # A function itself isn't an l-value
+ elif type.is_cpp_class and not type.is_fake_reference and self.scope.directives['cpp_locals']:
+ self.scope.use_utility_code(UtilityCode.load_cached("OptionalLocals", "CppSupport.cpp"))
if not type.is_pyobject and not type.is_memoryviewslice:
# Make manage_ref canonical, so that manage_ref will always mean
# a decref is needed.
manage_ref = False
freelist = self.temps_free.get((type, manage_ref))
- if freelist is not None and freelist[0]:
+ if reusable and freelist is not None and freelist[0]:
result = freelist[0].pop()
freelist[1].remove(result)
else:
@@ -730,9 +869,11 @@
result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
if result not in self.names_taken: break
self.temps_allocated.append((result, type, manage_ref, static))
+ if not reusable:
+ self.zombie_temps.add(result)
self.temps_used_type[result] = (type, manage_ref)
if DebugFlags.debug_temp_code_comments:
- self.owner.putln("/* %s allocated (%s) */" % (result, type))
+ self.owner.putln("/* %s allocated (%s)%s */" % (result, type, "" if reusable else " - zombie"))
if self.collect_temps_stack:
self.collect_temps_stack[-1].add((result, type))
@@ -751,10 +892,12 @@
self.temps_free[(type, manage_ref)] = freelist
if name in freelist[1]:
raise RuntimeError("Temp %s freed twice!" % name)
- freelist[0].append(name)
+ if name not in self.zombie_temps:
+ freelist[0].append(name)
freelist[1].add(name)
if DebugFlags.debug_temp_code_comments:
- self.owner.putln("/* %s released */" % name)
+ self.owner.putln("/* %s released %s*/" % (
+ name, " - zombie" if name in self.zombie_temps else ""))
def temps_in_use(self):
"""Return a list of (cname,type,manage_ref) tuples of temp names and their type
@@ -774,7 +917,7 @@
"""
return [(name, type)
for name, type, manage_ref in self.temps_in_use()
- if manage_ref and type.is_pyobject]
+ if manage_ref and type.is_pyobject]
def all_managed_temps(self):
"""Return a list of (cname, type) tuples of refcount-managed Python objects.
@@ -789,9 +932,11 @@
try-except and try-finally blocks to clean up temps in the
error case.
"""
- return [(cname, type)
- for (type, manage_ref), freelist in self.temps_free.items() if manage_ref
- for cname in freelist[0]]
+ return sorted([ # Enforce deterministic order.
+ (cname, type)
+ for (type, manage_ref), freelist in self.temps_free.items() if manage_ref
+ for cname in freelist[0]
+ ])
def start_collecting_temps(self):
"""
@@ -977,29 +1122,45 @@
'h_code',
'filename_table',
'utility_code_proto_before_types',
- 'numeric_typedefs', # Let these detailed individual parts stay!,
- 'complex_type_declarations', # as the proper solution is to make a full DAG...
- 'type_declarations', # More coarse-grained blocks would simply hide
- 'utility_code_proto', # the ugliness, not fix it
+ 'numeric_typedefs', # Let these detailed individual parts stay!,
+ 'complex_type_declarations', # as the proper solution is to make a full DAG...
+ 'type_declarations', # More coarse-grained blocks would simply hide
+ 'utility_code_proto', # the ugliness, not fix it
'module_declarations',
'typeinfo',
'before_global_var',
'global_var',
'string_decls',
'decls',
- 'all_the_rest',
+ 'late_includes',
+ 'module_state',
+ 'module_state_clear',
+ 'module_state_traverse',
+ 'module_state_defines', # redefines names used in module_state/_clear/_traverse
+ 'module_code', # user code goes here
'pystring_table',
'cached_builtins',
'cached_constants',
- 'init_globals',
+ 'init_constants',
+ 'init_globals', # (utility code called at init-time)
'init_module',
'cleanup_globals',
'cleanup_module',
'main_method',
+ 'utility_code_pragmas', # silence some irrelevant warnings in utility code
'utility_code_def',
+ 'utility_code_pragmas_end', # clean-up the utility_code_pragmas
'end'
]
+ # h files can only have a much smaller list of sections
+ h_code_layout = [
+ 'h_code',
+ 'utility_code_proto_before_types',
+ 'type_declarations',
+ 'utility_code_proto',
+ 'end'
+ ]
def __init__(self, writer, module_node, code_config, common_utility_include_dir=None):
self.filename_table = {}
@@ -1011,42 +1172,51 @@
self.code_config = code_config
self.common_utility_include_dir = common_utility_include_dir
self.parts = {}
- self.module_node = module_node # because some utility code generation needs it
- # (generating backwards-compatible Get/ReleaseBuffer
+ self.module_node = module_node # because some utility code generation needs it
+ # (generating backwards-compatible Get/ReleaseBuffer
self.const_cnames_used = {}
self.string_const_index = {}
+ self.dedup_const_index = {}
self.pyunicode_ptr_const_index = {}
self.num_const_index = {}
self.py_constants = []
self.cached_cmethods = {}
+ self.initialised_constants = set()
writer.set_global_state(self)
self.rootwriter = writer
def initialize_main_c_code(self):
rootwriter = self.rootwriter
- for part in self.code_layout:
- self.parts[part] = rootwriter.insertion_point()
+ for i, part in enumerate(self.code_layout):
+ w = self.parts[part] = rootwriter.insertion_point()
+ if i > 0:
+ w.putln("/* #### Code section: %s ### */" % part)
if not Options.cache_builtins:
del self.parts['cached_builtins']
else:
w = self.parts['cached_builtins']
w.enter_cfunc_scope()
- w.putln("static int __Pyx_InitCachedBuiltins(void) {")
+ w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) {")
w = self.parts['cached_constants']
w.enter_cfunc_scope()
w.putln("")
- w.putln("static int __Pyx_InitCachedConstants(void) {")
+ w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {")
w.put_declare_refcount_context()
- w.put_setup_refcount_context("__Pyx_InitCachedConstants")
+ w.put_setup_refcount_context(StringEncoding.EncodedString("__Pyx_InitCachedConstants"))
w = self.parts['init_globals']
w.enter_cfunc_scope()
w.putln("")
- w.putln("static int __Pyx_InitGlobals(void) {")
+ w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) {")
+
+ w = self.parts['init_constants']
+ w.enter_cfunc_scope()
+ w.putln("")
+ w.putln("static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {")
if not Options.generate_cleanup_code:
del self.parts['cleanup_globals']
@@ -1054,7 +1224,7 @@
w = self.parts['cleanup_globals']
w.enter_cfunc_scope()
w.putln("")
- w.putln("static void __Pyx_CleanupGlobals(void) {")
+ w.putln("static CYTHON_SMALL_CODE void __Pyx_CleanupGlobals(void) {")
code = self.parts['utility_code_proto']
code.putln("")
@@ -1066,6 +1236,11 @@
code.putln("")
code.putln("/* --- Runtime support code --- */")
+ def initialize_main_h_code(self):
+ rootwriter = self.rootwriter
+ for part in self.h_code_layout:
+ self.parts[part] = rootwriter.insertion_point()
+
def finalize_main_c_code(self):
self.close_global_decls()
@@ -1077,6 +1252,18 @@
code.put(util.format_code(util.impl))
code.putln("")
+ #
+ # utility code pragmas
+ #
+ code = self.parts['utility_code_pragmas']
+ util = UtilityCode.load_cached("UtilityCodePragmas", "ModuleSetupCode.c")
+ code.putln(util.format_code(util.impl))
+ code.putln("")
+ code = self.parts['utility_code_pragmas_end']
+ util = UtilityCode.load_cached("UtilityCodePragmasEnd", "ModuleSetupCode.c")
+ code.putln(util.format_code(util.impl))
+ code.putln("")
+
def __getitem__(self, key):
return self.parts[key]
@@ -1106,13 +1293,14 @@
w.putln("}")
w.exit_cfunc_scope()
- w = self.parts['init_globals']
- w.putln("return 0;")
- if w.label_used(w.error_label):
- w.put_label(w.error_label)
- w.putln("return -1;")
- w.putln("}")
- w.exit_cfunc_scope()
+ for part in ['init_globals', 'init_constants']:
+ w = self.parts[part]
+ w.putln("return 0;")
+ if w.label_used(w.error_label):
+ w.put_label(w.error_label)
+ w.putln("return -1;")
+ w.putln("}")
+ w.exit_cfunc_scope()
if Options.generate_cleanup_code:
w = self.parts['cleanup_globals']
@@ -1129,7 +1317,12 @@
# constant handling at code generation time
- def get_cached_constants_writer(self):
+ def get_cached_constants_writer(self, target=None):
+ if target is not None:
+ if target in self.initialised_constants:
+ # Return None on second/later calls to prevent duplicate creation code.
+ return None
+ self.initialised_constants.add(target)
return self.parts['cached_constants']
def get_int_const(self, str_value, longness=False):
@@ -1147,13 +1340,19 @@
c = self.new_num_const(str_value, 'float', value_code)
return c
- def get_py_const(self, type, prefix='', cleanup_level=None):
+ def get_py_const(self, type, prefix='', cleanup_level=None, dedup_key=None):
+ if dedup_key is not None:
+ const = self.dedup_const_index.get(dedup_key)
+ if const is not None:
+ return const
# create a new Python object constant
const = self.new_py_const(type, prefix)
if cleanup_level is not None \
and cleanup_level <= Options.generate_cleanup_code:
cleanup_writer = self.parts['cleanup_globals']
cleanup_writer.putln('Py_CLEAR(%s);' % const.cname)
+ if dedup_key is not None:
+ self.dedup_const_index[dedup_key] = const
return const
def get_string_const(self, text, py_version=None):
@@ -1241,8 +1440,8 @@
prefix = Naming.const_prefix
return "%s%s" % (prefix, name_suffix)
- def get_cached_unbound_method(self, type_cname, method_name, args_count):
- key = (type_cname, method_name, args_count)
+ def get_cached_unbound_method(self, type_cname, method_name):
+ key = (type_cname, method_name)
try:
cname = self.cached_cmethods[key]
except KeyError:
@@ -1250,6 +1449,18 @@
'umethod', '%s_%s' % (type_cname, method_name))
return cname
+ def cached_unbound_method_call_code(self, obj_cname, type_cname, method_name, arg_cnames):
+ # admittedly, not the best place to put this method, but it is reused by UtilityCode and ExprNodes ...
+ utility_code_name = "CallUnboundCMethod%d" % len(arg_cnames)
+ self.use_utility_code(UtilityCode.load_cached(utility_code_name, "ObjectHandling.c"))
+ cache_cname = self.get_cached_unbound_method(type_cname, method_name)
+ args = [obj_cname] + arg_cnames
+ return "__Pyx_%s(&%s, %s)" % (
+ utility_code_name,
+ cache_cname,
+ ', '.join(args),
+ )
+
def add_cached_builtin_decl(self, entry):
if entry.is_builtin and entry.is_const:
if self.should_declare(entry.cname, entry):
@@ -1291,25 +1502,37 @@
for c in self.py_constants]
consts.sort()
decls_writer = self.parts['decls']
+ decls_writer.putln("#if !CYTHON_USE_MODULE_STATE")
for _, cname, c in consts:
+ self.parts['module_state'].putln("%s;" % c.type.declaration_code(cname))
+ self.parts['module_state_defines'].putln(
+ "#define %s %s->%s" % (cname, Naming.modulestateglobal_cname, cname))
+ self.parts['module_state_clear'].putln(
+ "Py_CLEAR(clear_module_state->%s);" % cname)
+ self.parts['module_state_traverse'].putln(
+ "Py_VISIT(traverse_module_state->%s);" % cname)
decls_writer.putln(
"static %s;" % c.type.declaration_code(cname))
+ decls_writer.putln("#endif")
def generate_cached_methods_decls(self):
if not self.cached_cmethods:
return
decl = self.parts['decls']
- init = self.parts['init_globals']
+ init = self.parts['init_constants']
cnames = []
- for (type_cname, method_name, _), cname in sorted(self.cached_cmethods.items()):
+ for (type_cname, method_name), cname in sorted(self.cached_cmethods.items()):
cnames.append(cname)
method_name_cname = self.get_interned_identifier(StringEncoding.EncodedString(method_name)).cname
- decl.putln('static __Pyx_CachedCFunction %s = {0, &%s, 0, 0, 0};' % (
- cname, method_name_cname))
+ decl.putln('static __Pyx_CachedCFunction %s = {0, 0, 0, 0, 0};' % (
+ cname))
# split type reference storage as it might not be static
init.putln('%s.type = (PyObject*)&%s;' % (
cname, type_cname))
+ # method name string isn't static in limited api
+ init.putln('%s.method_name = &%s;' % (
+ cname, method_name_cname))
if Options.generate_cleanup_code:
cleanup = self.parts['cleanup_globals']
@@ -1347,13 +1570,26 @@
decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf16_array))
decls_writer.putln("#endif")
+ init_constants = self.parts['init_constants']
if py_strings:
self.use_utility_code(UtilityCode.load_cached("InitStrings", "StringTools.c"))
py_strings.sort()
w = self.parts['pystring_table']
w.putln("")
w.putln("static __Pyx_StringTabEntry %s[] = {" % Naming.stringtab_cname)
- for c_cname, _, py_string in py_strings:
+ w.putln("#if CYTHON_USE_MODULE_STATE")
+ w_in_module_state = w.insertion_point()
+ w.putln("#else")
+ w_not_in_module_state = w.insertion_point()
+ w.putln("#endif")
+ decls_writer.putln("#if !CYTHON_USE_MODULE_STATE")
+ not_limited_api_decls_writer = decls_writer.insertion_point()
+ decls_writer.putln("#endif")
+ init_constants.putln("#if CYTHON_USE_MODULE_STATE")
+ init_constants_in_module_state = init_constants.insertion_point()
+ init_constants.putln("#endif")
+ for idx, py_string_args in enumerate(py_strings):
+ c_cname, _, py_string = py_string_args
if not py_string.is_str or not py_string.encoding or \
py_string.encoding in ('ASCII', 'USASCII', 'US-ASCII',
'UTF8', 'UTF-8'):
@@ -1361,19 +1597,28 @@
else:
encoding = '"%s"' % py_string.encoding.lower()
- decls_writer.putln(
+ self.parts['module_state'].putln("PyObject *%s;" % py_string.cname)
+ self.parts['module_state_defines'].putln("#define %s %s->%s" % (
+ py_string.cname,
+ Naming.modulestateglobal_cname,
+ py_string.cname))
+ self.parts['module_state_clear'].putln("Py_CLEAR(clear_module_state->%s);" %
+ py_string.cname)
+ self.parts['module_state_traverse'].putln("Py_VISIT(traverse_module_state->%s);" %
+ py_string.cname)
+ not_limited_api_decls_writer.putln(
"static PyObject *%s;" % py_string.cname)
if py_string.py3str_cstring:
- w.putln("#if PY_MAJOR_VERSION >= 3")
- w.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
+ w_not_in_module_state.putln("#if PY_MAJOR_VERSION >= 3")
+ w_not_in_module_state.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
py_string.cname,
py_string.py3str_cstring.cname,
py_string.py3str_cstring.cname,
'0', 1, 0,
py_string.intern
))
- w.putln("#else")
- w.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
+ w_not_in_module_state.putln("#else")
+ w_not_in_module_state.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
py_string.cname,
c_cname,
c_cname,
@@ -1383,24 +1628,46 @@
py_string.intern
))
if py_string.py3str_cstring:
- w.putln("#endif")
+ w_not_in_module_state.putln("#endif")
+ w_in_module_state.putln("{0, %s, sizeof(%s), %s, %d, %d, %d}," % (
+ c_cname if not py_string.py3str_cstring else py_string.py3str_cstring.cname,
+ c_cname if not py_string.py3str_cstring else py_string.py3str_cstring.cname,
+ encoding if not py_string.py3str_cstring else '0',
+ py_string.is_unicode,
+ py_string.is_str,
+ py_string.intern
+ ))
+ init_constants_in_module_state.putln("if (__Pyx_InitString(%s[%d], &%s) < 0) %s;" % (
+ Naming.stringtab_cname,
+ idx,
+ py_string.cname,
+ init_constants.error_goto(self.module_pos)))
w.putln("{0, 0, 0, 0, 0, 0, 0}")
w.putln("};")
- init_globals = self.parts['init_globals']
- init_globals.putln(
+ init_constants.putln("#if !CYTHON_USE_MODULE_STATE")
+ init_constants.putln(
"if (__Pyx_InitStrings(%s) < 0) %s;" % (
Naming.stringtab_cname,
- init_globals.error_goto(self.module_pos)))
+ init_constants.error_goto(self.module_pos)))
+ init_constants.putln("#endif")
def generate_num_constants(self):
consts = [(c.py_type, c.value[0] == '-', len(c.value), c.value, c.value_code, c)
for c in self.num_const_index.values()]
consts.sort()
decls_writer = self.parts['decls']
- init_globals = self.parts['init_globals']
+ decls_writer.putln("#if !CYTHON_USE_MODULE_STATE")
+ init_constants = self.parts['init_constants']
for py_type, _, _, value, value_code, c in consts:
cname = c.cname
+ self.parts['module_state'].putln("PyObject *%s;" % cname)
+ self.parts['module_state_defines'].putln("#define %s %s->%s" % (
+ cname, Naming.modulestateglobal_cname, cname))
+ self.parts['module_state_clear'].putln(
+ "Py_CLEAR(clear_module_state->%s);" % cname)
+ self.parts['module_state_traverse'].putln(
+ "Py_VISIT(traverse_module_state->%s);" % cname)
decls_writer.putln("static PyObject *%s;" % cname)
if py_type == 'float':
function = 'PyFloat_FromDouble(%s)'
@@ -1412,9 +1679,10 @@
function = "PyInt_FromLong(%sL)"
else:
function = "PyInt_FromLong(%s)"
- init_globals.putln('%s = %s; %s' % (
+ init_constants.putln('%s = %s; %s' % (
cname, function % value_code,
- init_globals.error_goto_if_null(cname, self.module_pos)))
+ init_constants.error_goto_if_null(cname, self.module_pos)))
+ decls_writer.putln("#endif")
# The functions below are there in a transition phase only
# and will be deprecated. They are called from Nodes.BlockNode.
@@ -1436,12 +1704,13 @@
#
def lookup_filename(self, source_desc):
+ entry = source_desc.get_filenametable_entry()
try:
- index = self.filename_table[source_desc.get_filenametable_entry()]
+ index = self.filename_table[entry]
except KeyError:
index = len(self.filename_list)
self.filename_list.append(source_desc)
- self.filename_table[source_desc.get_filenametable_entry()] = index
+ self.filename_table[entry] = index
return index
def commented_file_contents(self, source_desc):
@@ -1478,7 +1747,7 @@
See UtilityCode.
"""
- if utility_code not in self.utility_codes:
+ if utility_code and utility_code not in self.utility_codes:
self.utility_codes.add(utility_code)
utility_code.put_code(self)
@@ -1491,7 +1760,8 @@
self.use_utility_code(entry.utility_code_definition)
-def funccontext_property(name):
+def funccontext_property(func):
+ name = func.__name__
attribute_of = operator.attrgetter(name)
def get(self):
return attribute_of(self.funcstate)
@@ -1521,7 +1791,7 @@
as well
- labels, temps, exc_vars: One must construct a scope in which these can
exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for
- sanity checking and forward compatabilty). Created insertion points
+ sanity checking and forward compatibility). Created insertion points
looses this scope and cannot access it.
- marker: Not copied to insertion point
- filename_table, filename_list, input_file_contents: All codewriters
@@ -1542,8 +1812,7 @@
# about the current class one is in
# code_config CCodeConfig configuration options for the C code writer
- globalstate = code_config = None
-
+ @cython.locals(create_from='CCodeWriter')
def __init__(self, create_from=None, buffer=None, copy_formatting=False):
if buffer is None: buffer = StringIOTree()
self.buffer = buffer
@@ -1552,6 +1821,8 @@
self.pyclass_stack = []
self.funcstate = None
+ self.globalstate = None
+ self.code_config = None
self.level = 0
self.call_level = 0
self.bol = 1
@@ -1586,10 +1857,13 @@
return self.buffer.getvalue()
def write(self, s):
- # also put invalid markers (lineno 0), to indicate that those lines
- # have no Cython source code correspondence
- cython_lineno = self.last_marked_pos[1] if self.last_marked_pos else 0
- self.buffer.markers.extend([cython_lineno] * s.count('\n'))
+ # Cygdb needs to know which Cython source line corresponds to which C line.
+ # Therefore, we write this information into "self.buffer.markers" and then write it from there
+ # into cython_debug/cython_debug_info_* (see ModuleNode._serialize_lineno_map).
+
+ filename_line = self.last_marked_pos[:2] if self.last_marked_pos else (None, 0)
+ self.buffer.markers.extend([filename_line] * s.count('\n'))
+
self.buffer.write(s)
def insertion_point(self):
@@ -1614,19 +1888,27 @@
self.buffer.insert(writer.buffer)
# Properties delegated to function scope
- label_counter = funccontext_property("label_counter")
- return_label = funccontext_property("return_label")
- error_label = funccontext_property("error_label")
- labels_used = funccontext_property("labels_used")
- continue_label = funccontext_property("continue_label")
- break_label = funccontext_property("break_label")
- return_from_error_cleanup_label = funccontext_property("return_from_error_cleanup_label")
- yield_labels = funccontext_property("yield_labels")
+ @funccontext_property
+ def label_counter(self): pass
+ @funccontext_property
+ def return_label(self): pass
+ @funccontext_property
+ def error_label(self): pass
+ @funccontext_property
+ def labels_used(self): pass
+ @funccontext_property
+ def continue_label(self): pass
+ @funccontext_property
+ def break_label(self): pass
+ @funccontext_property
+ def return_from_error_cleanup_label(self): pass
+ @funccontext_property
+ def yield_labels(self): pass
# Functions delegated to function scope
def new_label(self, name=None): return self.funcstate.new_label(name)
def new_error_label(self): return self.funcstate.new_error_label()
- def new_yield_label(self): return self.funcstate.new_yield_label()
+ def new_yield_label(self, *args): return self.funcstate.new_yield_label(*args)
def get_loop_labels(self): return self.funcstate.get_loop_labels()
def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels)
def new_loop_labels(self): return self.funcstate.new_loop_labels()
@@ -1641,6 +1923,7 @@
self.funcstate = FunctionState(self, scope=scope)
def exit_cfunc_scope(self):
+ self.funcstate.validate_exit()
self.funcstate = None
# constant handling
@@ -1651,8 +1934,8 @@
def get_py_float(self, str_value, value_code):
return self.globalstate.get_float_const(str_value, value_code).cname
- def get_py_const(self, type, prefix='', cleanup_level=None):
- return self.globalstate.get_py_const(type, prefix, cleanup_level).cname
+ def get_py_const(self, type, prefix='', cleanup_level=None, dedup_key=None):
+ return self.globalstate.get_py_const(type, prefix, cleanup_level, dedup_key).cname
def get_string_const(self, text):
return self.globalstate.get_string_const(text).cname
@@ -1674,8 +1957,8 @@
def intern_identifier(self, text):
return self.get_py_string_const(text, identifier=True)
- def get_cached_constants_writer(self):
- return self.globalstate.get_cached_constants_writer()
+ def get_cached_constants_writer(self, target=None):
+ return self.globalstate.get_cached_constants_writer(target)
# code generation
@@ -1731,19 +2014,17 @@
include_dir = self.globalstate.common_utility_include_dir
if include_dir and len(code) > 1024:
include_file = "%s_%s.h" % (
- name, hashlib.md5(code.encode('utf8')).hexdigest())
+ name, hashlib.sha1(code.encode('utf8')).hexdigest())
path = os.path.join(include_dir, include_file)
if not os.path.exists(path):
tmp_path = '%s.tmp%s' % (path, os.getpid())
with closing(Utils.open_new_file(tmp_path)) as f:
f.write(code)
- os.rename(tmp_path, path)
+ shutil.move(tmp_path, path)
code = '#include "%s"\n' % path
self.put(code)
def put(self, code):
- if is_self_assignment(code):
- return
fix_indent = False
if "{" in code:
dl = code.count("{")
@@ -1815,22 +2096,29 @@
self.put("%s " % storage_class)
if not entry.cf_used:
self.put('CYTHON_UNUSED ')
- self.put(entry.type.declaration_code(
- entry.cname, dll_linkage=dll_linkage))
+ if entry.is_cpp_optional:
+ self.put(entry.type.cpp_optional_declaration_code(
+ entry.cname, dll_linkage=dll_linkage))
+ else:
+ self.put(entry.type.declaration_code(
+ entry.cname, dll_linkage=dll_linkage))
if entry.init is not None:
self.put_safe(" = %s" % entry.type.literal_code(entry.init))
elif entry.type.is_pyobject:
self.put(" = NULL")
self.putln(";")
+ self.funcstate.scope.use_entry_utility_code(entry)
def put_temp_declarations(self, func_context):
for name, type, manage_ref, static in func_context.temps_allocated:
- decl = type.declaration_code(name)
+ if type.is_cpp_class and not type.is_fake_reference and func_context.scope.directives['cpp_locals']:
+ decl = type.cpp_optional_declaration_code(name)
+ else:
+ decl = type.declaration_code(name)
if type.is_pyobject:
self.putln("%s = NULL;" % decl)
elif type.is_memoryviewslice:
- from . import MemoryView
- self.putln("%s = %s;" % (decl, MemoryView.memslice_entry_init))
+ self.putln("%s = %s;" % (decl, type.literal_code(type.default_value)))
else:
self.putln("%s%s;" % (static and "static " or "", decl))
@@ -1845,7 +2133,7 @@
self.putln("%sint %s = 0;" % (unused, Naming.clineno_cname))
def put_generated_by(self):
- self.putln("/* Generated by Cython %s */" % Version.watermark)
+ self.putln(Utils.GENERATED_BY_MARKER)
self.putln("")
def put_h_guard(self, guard):
@@ -1868,7 +2156,7 @@
def entry_as_pyobject(self, entry):
type = entry.type
if (not entry.is_self_arg and not entry.type.is_complete()
- or entry.type.is_extension_type):
+ or entry.type.is_extension_type):
return "(PyObject *)" + entry.cname
else:
return entry.cname
@@ -1877,117 +2165,89 @@
from .PyrexTypes import py_object_type, typecast
return typecast(py_object_type, type, cname)
- def put_gotref(self, cname):
- self.putln("__Pyx_GOTREF(%s);" % cname)
+ def put_gotref(self, cname, type):
+ type.generate_gotref(self, cname)
- def put_giveref(self, cname):
- self.putln("__Pyx_GIVEREF(%s);" % cname)
+ def put_giveref(self, cname, type):
+ type.generate_giveref(self, cname)
- def put_xgiveref(self, cname):
- self.putln("__Pyx_XGIVEREF(%s);" % cname)
+ def put_xgiveref(self, cname, type):
+ type.generate_xgiveref(self, cname)
- def put_xgotref(self, cname):
- self.putln("__Pyx_XGOTREF(%s);" % cname)
+ def put_xgotref(self, cname, type):
+ type.generate_xgotref(self, cname)
def put_incref(self, cname, type, nanny=True):
- if nanny:
- self.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname, type))
- else:
- self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
+ # Note: original put_Memslice_Incref/Decref also added in some utility code
+ # this is unnecessary since the relevant utility code is loaded anyway if a memoryview is used
+ # and so has been removed. However, it's potentially a feature that might be useful here
+ type.generate_incref(self, cname, nanny=nanny)
+
+ def put_xincref(self, cname, type, nanny=True):
+ type.generate_xincref(self, cname, nanny=nanny)
+
+ def put_decref(self, cname, type, nanny=True, have_gil=True):
+ type.generate_decref(self, cname, nanny=nanny, have_gil=have_gil)
+
+ def put_xdecref(self, cname, type, nanny=True, have_gil=True):
+ type.generate_xdecref(self, cname, nanny=nanny, have_gil=have_gil)
+
+ def put_decref_clear(self, cname, type, clear_before_decref=False, nanny=True, have_gil=True):
+ type.generate_decref_clear(self, cname, clear_before_decref=clear_before_decref,
+ nanny=nanny, have_gil=have_gil)
+
+ def put_xdecref_clear(self, cname, type, clear_before_decref=False, nanny=True, have_gil=True):
+ type.generate_xdecref_clear(self, cname, clear_before_decref=clear_before_decref,
+ nanny=nanny, have_gil=have_gil)
+
+ def put_decref_set(self, cname, type, rhs_cname):
+ type.generate_decref_set(self, cname, rhs_cname)
+
+ def put_xdecref_set(self, cname, type, rhs_cname):
+ type.generate_xdecref_set(self, cname, rhs_cname)
- def put_decref(self, cname, type, nanny=True):
- self._put_decref(cname, type, nanny, null_check=False, clear=False)
+ def put_incref_memoryviewslice(self, slice_cname, type, have_gil):
+ # TODO ideally this would just be merged into "put_incref"
+ type.generate_incref_memoryviewslice(self, slice_cname, have_gil=have_gil)
+
+ def put_var_incref_memoryviewslice(self, entry, have_gil):
+ self.put_incref_memoryviewslice(entry.cname, entry.type, have_gil=have_gil)
def put_var_gotref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_GOTREF(%s);" % self.entry_as_pyobject(entry))
+ self.put_gotref(entry.cname, entry.type)
def put_var_giveref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_GIVEREF(%s);" % self.entry_as_pyobject(entry))
+ self.put_giveref(entry.cname, entry.type)
def put_var_xgotref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XGOTREF(%s);" % self.entry_as_pyobject(entry))
+ self.put_xgotref(entry.cname, entry.type)
def put_var_xgiveref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XGIVEREF(%s);" % self.entry_as_pyobject(entry))
+ self.put_xgiveref(entry.cname, entry.type)
- def put_var_incref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
-
- def put_var_xincref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XINCREF(%s);" % self.entry_as_pyobject(entry))
-
- def put_decref_clear(self, cname, type, nanny=True, clear_before_decref=False):
- self._put_decref(cname, type, nanny, null_check=False,
- clear=True, clear_before_decref=clear_before_decref)
+ def put_var_incref(self, entry, **kwds):
+ self.put_incref(entry.cname, entry.type, **kwds)
- def put_xdecref(self, cname, type, nanny=True, have_gil=True):
- self._put_decref(cname, type, nanny, null_check=True,
- have_gil=have_gil, clear=False)
+ def put_var_xincref(self, entry, **kwds):
+ self.put_xincref(entry.cname, entry.type, **kwds)
- def put_xdecref_clear(self, cname, type, nanny=True, clear_before_decref=False):
- self._put_decref(cname, type, nanny, null_check=True,
- clear=True, clear_before_decref=clear_before_decref)
-
- def _put_decref(self, cname, type, nanny=True, null_check=False,
- have_gil=True, clear=False, clear_before_decref=False):
- if type.is_memoryviewslice:
- self.put_xdecref_memoryviewslice(cname, have_gil=have_gil)
- return
+ def put_var_decref(self, entry, **kwds):
+ self.put_decref(entry.cname, entry.type, **kwds)
- prefix = nanny and '__Pyx' or 'Py'
- X = null_check and 'X' or ''
+ def put_var_xdecref(self, entry, **kwds):
+ self.put_xdecref(entry.cname, entry.type, **kwds)
- if clear:
- if clear_before_decref:
- if not nanny:
- X = '' # CPython doesn't have a Py_XCLEAR()
- self.putln("%s_%sCLEAR(%s);" % (prefix, X, cname))
- else:
- self.putln("%s_%sDECREF(%s); %s = 0;" % (
- prefix, X, self.as_pyobject(cname, type), cname))
- else:
- self.putln("%s_%sDECREF(%s);" % (
- prefix, X, self.as_pyobject(cname, type)))
+ def put_var_decref_clear(self, entry, **kwds):
+ self.put_decref_clear(entry.cname, entry.type, clear_before_decref=entry.in_closure, **kwds)
- def put_decref_set(self, cname, rhs_cname):
- self.putln("__Pyx_DECREF_SET(%s, %s);" % (cname, rhs_cname))
+ def put_var_decref_set(self, entry, rhs_cname, **kwds):
+ self.put_decref_set(entry.cname, entry.type, rhs_cname, **kwds)
- def put_xdecref_set(self, cname, rhs_cname):
- self.putln("__Pyx_XDECREF_SET(%s, %s);" % (cname, rhs_cname))
-
- def put_var_decref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
-
- def put_var_xdecref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
-
- def put_var_decref_clear(self, entry):
- self._put_var_decref_clear(entry, null_check=False)
-
- def put_var_xdecref_clear(self, entry):
- self._put_var_decref_clear(entry, null_check=True)
-
- def _put_var_decref_clear(self, entry, null_check):
- if entry.type.is_pyobject:
- if entry.in_closure:
- # reset before DECREF to make sure closure state is
- # consistent during call to DECREF()
- self.putln("__Pyx_%sCLEAR(%s);" % (
- null_check and 'X' or '',
- entry.cname))
- else:
- self.putln("__Pyx_%sDECREF(%s); %s = 0;" % (
- null_check and 'X' or '',
- self.entry_as_pyobject(entry),
- entry.cname))
+ def put_var_xdecref_set(self, entry, rhs_cname, **kwds):
+ self.put_xdecref_set(entry.cname, entry.type, rhs_cname, **kwds)
+
+ def put_var_xdecref_clear(self, entry, **kwds):
+ self.put_xdecref_clear(entry.cname, entry.type, clear_before_decref=entry.in_closure, **kwds)
def put_var_decrefs(self, entries, used_only = 0):
for entry in entries:
@@ -2005,19 +2265,6 @@
for entry in entries:
self.put_var_xdecref_clear(entry)
- def put_incref_memoryviewslice(self, slice_cname, have_gil=False):
- from . import MemoryView
- self.globalstate.use_utility_code(MemoryView.memviewslice_init_code)
- self.putln("__PYX_INC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil)))
-
- def put_xdecref_memoryviewslice(self, slice_cname, have_gil=False):
- from . import MemoryView
- self.globalstate.use_utility_code(MemoryView.memviewslice_init_code)
- self.putln("__PYX_XDEC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil)))
-
- def put_xgiveref_memoryviewslice(self, slice_cname):
- self.put_xgiveref("%s.memview" % slice_cname)
-
def put_init_to_py_none(self, cname, type, nanny=True):
from .PyrexTypes import py_object_type, typecast
py_none = typecast(type, py_object_type, "Py_None")
@@ -2034,7 +2281,7 @@
if entry.in_closure:
self.put_giveref('Py_None')
- def put_pymethoddef(self, entry, term, allow_skip=True):
+ def put_pymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=None):
if entry.is_special or entry.name == '__getattribute__':
if entry.name not in special_py_methods:
if entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']:
@@ -2044,25 +2291,47 @@
# that's better than ours.
elif allow_skip:
return
- from .TypeSlots import method_coexist
- if entry.doc:
- doc_code = entry.doc_cname
- else:
- doc_code = 0
+
method_flags = entry.signature.method_flags()
- if method_flags:
- if entry.is_special:
- method_flags += [method_coexist]
- self.putln(
- '{"%s", (PyCFunction)%s, %s, %s}%s' % (
- entry.name,
- entry.func_cname,
- "|".join(method_flags),
- doc_code,
- term))
+ if not method_flags:
+ return
+ if entry.is_special:
+ from . import TypeSlots
+ method_flags += [TypeSlots.method_coexist]
+ func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
+ # Add required casts, but try not to shadow real warnings.
+ cast = entry.signature.method_function_type()
+ if cast != 'PyCFunction':
+ func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
+ entry_name = entry.name.as_c_string_literal()
+ self.putln(
+ '{%s, (PyCFunction)%s, %s, %s}%s' % (
+ entry_name,
+ func_ptr,
+ "|".join(method_flags),
+ entry.doc_cname if entry.doc else '0',
+ term))
+
+ def put_pymethoddef_wrapper(self, entry):
+ func_cname = entry.func_cname
+ if entry.is_special:
+ method_flags = entry.signature.method_flags() or []
+ from .TypeSlots import method_noargs
+ if method_noargs in method_flags:
+ # Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one.
+ func_cname = Naming.method_wrapper_prefix + func_cname
+ self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {return %s(self);}" % (
+ func_cname, entry.func_cname))
+ return func_cname
# GIL methods
+ def use_fast_gil_utility_code(self):
+ if self.globalstate.directives['fast_gil']:
+ self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
+ else:
+ self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
+
def put_ensure_gil(self, declare_gilstate=True, variable=None):
"""
Acquire the GIL. The generated code is safe even when no PyThreadState
@@ -2072,42 +2341,59 @@
"""
self.globalstate.use_utility_code(
UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c"))
+ self.use_fast_gil_utility_code()
self.putln("#ifdef WITH_THREAD")
if not variable:
variable = '__pyx_gilstate_save'
if declare_gilstate:
self.put("PyGILState_STATE ")
- self.putln("%s = PyGILState_Ensure();" % variable)
+ self.putln("%s = __Pyx_PyGILState_Ensure();" % variable)
self.putln("#endif")
def put_release_ensured_gil(self, variable=None):
"""
Releases the GIL, corresponds to `put_ensure_gil`.
"""
+ self.use_fast_gil_utility_code()
if not variable:
variable = '__pyx_gilstate_save'
self.putln("#ifdef WITH_THREAD")
- self.putln("PyGILState_Release(%s);" % variable)
+ self.putln("__Pyx_PyGILState_Release(%s);" % variable)
self.putln("#endif")
- def put_acquire_gil(self, variable=None):
+ def put_acquire_gil(self, variable=None, unknown_gil_state=True):
"""
Acquire the GIL. The thread's thread state must have been initialized
by a previous `put_release_gil`
"""
+ self.use_fast_gil_utility_code()
self.putln("#ifdef WITH_THREAD")
+ self.putln("__Pyx_FastGIL_Forget();")
if variable:
self.putln('_save = %s;' % variable)
+ if unknown_gil_state:
+ self.putln("if (_save) {")
self.putln("Py_BLOCK_THREADS")
+ if unknown_gil_state:
+ self.putln("}")
self.putln("#endif")
- def put_release_gil(self, variable=None):
+ def put_release_gil(self, variable=None, unknown_gil_state=True):
"Release the GIL, corresponds to `put_acquire_gil`."
+ self.use_fast_gil_utility_code()
self.putln("#ifdef WITH_THREAD")
self.putln("PyThreadState *_save;")
+ self.putln("_save = NULL;")
+ if unknown_gil_state:
+ # we don't *know* that we don't have the GIL (since we may be inside a nogil function,
+ # and Py_UNBLOCK_THREADS is unsafe without the GIL)
+ self.putln("if (PyGILState_Check()) {")
self.putln("Py_UNBLOCK_THREADS")
+ if unknown_gil_state:
+ self.putln("}")
if variable:
self.putln('%s = _save;' % variable)
+ self.putln("__Pyx_FastGIL_Remember();")
self.putln("#endif")
def declare_gilstate(self):
@@ -2118,26 +2404,38 @@
# error handling
def put_error_if_neg(self, pos, value):
-# return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos))) # TODO this path is almost _never_ taken, yet this macro makes is slower!
+ # TODO this path is almost _never_ taken, yet this macro makes is slower!
+ # return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos)))
return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
- def put_error_if_unbound(self, pos, entry, in_nogil_context=False):
- from . import ExprNodes
+ def put_error_if_unbound(self, pos, entry, in_nogil_context=False, unbound_check_code=None):
if entry.from_closure:
func = '__Pyx_RaiseClosureNameError'
self.globalstate.use_utility_code(
- ExprNodes.raise_closure_name_error_utility_code)
+ UtilityCode.load_cached("RaiseClosureNameError", "ObjectHandling.c"))
elif entry.type.is_memoryviewslice and in_nogil_context:
func = '__Pyx_RaiseUnboundMemoryviewSliceNogil'
self.globalstate.use_utility_code(
- ExprNodes.raise_unbound_memoryview_utility_code_nogil)
+ UtilityCode.load_cached("RaiseUnboundMemoryviewSliceNogil", "ObjectHandling.c"))
+ elif entry.type.is_cpp_class and entry.is_cglobal:
+ func = '__Pyx_RaiseCppGlobalNameError'
+ self.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseCppGlobalNameError", "ObjectHandling.c"))
+ elif entry.type.is_cpp_class and entry.is_variable and not entry.is_member and entry.scope.is_c_class_scope:
+ # there doesn't seem to be a good way to detecting an instance-attribute of a C class
+ # (is_member is only set for class attributes)
+ func = '__Pyx_RaiseCppAttributeError'
+ self.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseCppAttributeError", "ObjectHandling.c"))
else:
func = '__Pyx_RaiseUnboundLocalError'
self.globalstate.use_utility_code(
- ExprNodes.raise_unbound_local_error_utility_code)
+ UtilityCode.load_cached("RaiseUnboundLocalError", "ObjectHandling.c"))
+ if not unbound_check_code:
+ unbound_check_code = entry.type.check_for_null_code(entry.cname)
self.putln('if (unlikely(!%s)) { %s("%s"); %s }' % (
- entry.type.check_for_null_code(entry.cname),
+ unbound_check_code,
func,
entry.name,
self.error_goto(pos)))
@@ -2146,22 +2444,18 @@
self.funcstate.should_declare_error_indicator = True
if used:
self.funcstate.uses_error_indicator = True
- if self.code_config.c_line_in_traceback:
- cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
- else:
- cinfo = ""
-
- return "%s = %s[%s]; %s = %s;%s" % (
- Naming.filename_cname,
- Naming.filetable_cname,
+ return "__PYX_MARK_ERR_POS(%s, %s)" % (
self.lookup_filename(pos[0]),
- Naming.lineno_cname,
- pos[1],
- cinfo)
+ pos[1])
- def error_goto(self, pos):
+ def error_goto(self, pos, used=True):
lbl = self.funcstate.error_label
self.funcstate.use_label(lbl)
+ if pos is None:
+ return 'goto %s;' % lbl
+ self.funcstate.should_declare_error_indicator = True
+ if used:
+ self.funcstate.uses_error_indicator = True
return "__PYX_ERR(%s, %s, %s)" % (
self.lookup_filename(pos[0]),
pos[1],
@@ -2174,7 +2468,8 @@
return self.error_goto_if("!%s" % cname, pos)
def error_goto_if_neg(self, cname, pos):
- return self.error_goto_if("%s < 0" % cname, pos)
+ # Add extra parentheses to silence clang warnings about constant conditions.
+ return self.error_goto_if("(%s < 0)" % cname, pos)
def error_goto_if_PyErr(self, pos):
return self.error_goto_if("PyErr_Occurred()", pos)
@@ -2186,28 +2481,31 @@
self.putln('__Pyx_RefNannyDeclarations')
def put_setup_refcount_context(self, name, acquire_gil=False):
+ name = name.as_c_string_literal() # handle unicode names
if acquire_gil:
self.globalstate.use_utility_code(
UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c"))
- self.putln('__Pyx_RefNannySetupContext("%s", %d);' % (name, acquire_gil and 1 or 0))
+ self.putln('__Pyx_RefNannySetupContext(%s, %d);' % (name, acquire_gil and 1 or 0))
- def put_finish_refcount_context(self):
- self.putln("__Pyx_RefNannyFinishContext();")
+ def put_finish_refcount_context(self, nogil=False):
+ self.putln("__Pyx_RefNannyFinishContextNogil()" if nogil else "__Pyx_RefNannyFinishContext();")
- def put_add_traceback(self, qualified_name):
+ def put_add_traceback(self, qualified_name, include_cline=True):
"""
Build a Python traceback for propagating exceptions.
qualified_name should be the qualified name of the function.
"""
+ qualified_name = qualified_name.as_c_string_literal() # handle unicode names
format_tuple = (
qualified_name,
- Naming.clineno_cname,
+ Naming.clineno_cname if include_cline else 0,
Naming.lineno_cname,
Naming.filename_cname,
)
+
self.funcstate.uses_error_indicator = True
- self.putln('__Pyx_AddTraceback("%s", %s, %s, %s);' % format_tuple)
+ self.putln('__Pyx_AddTraceback(%s, %s, %s, %s);' % format_tuple)
def put_unraisable(self, qualified_name, nogil=False):
"""
@@ -2270,6 +2568,7 @@
self.putln(" #define unlikely(x) __builtin_expect(!!(x), 0)")
self.putln("#endif")
+
class PyrexCodeWriter(object):
# f file output file
# level int indentation level
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/CythonScope.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/CythonScope.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/CythonScope.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/CythonScope.py 2022-03-24 10:16:46.000000000 +0000
@@ -6,6 +6,7 @@
from .Errors import error
from .Scanning import StringSourceDescriptor
from . import MemoryView
+from .StringEncoding import EncodedString
class CythonScope(ModuleScope):
@@ -26,6 +27,10 @@
cname='')
entry.in_cinclude = True
+ def is_cpp(self):
+ # Allow C++ utility code in C++ contexts.
+ return self.context.cpp
+
def lookup_type(self, name):
# This function should go away when types are all first-level objects.
type = parse_basic_type(name)
@@ -121,10 +126,26 @@
view_utility_scope = MemoryView.view_utility_code.declare_in_scope(
self.viewscope, cython_scope=self,
- whitelist=MemoryView.view_utility_whitelist)
+ allowlist=MemoryView.view_utility_allowlist)
+
+ # Marks the types as being cython_builtin_type so that they can be
+ # extended from without Cython attempting to import cython.view
+ ext_types = [ entry.type
+ for entry in view_utility_scope.entries.values()
+ if entry.type.is_extension_type ]
+ for ext_type in ext_types:
+ ext_type.is_cython_builtin_type = 1
# self.entries["array"] = view_utility_scope.entries.pop("array")
+ # dataclasses scope
+ dc_str = EncodedString(u'dataclasses')
+ dataclassesscope = ModuleScope(dc_str, self, context=None)
+ self.declare_module(dc_str, dataclassesscope, pos=None).as_module = dataclassesscope
+ dataclassesscope.is_cython_builtin = True
+ dataclassesscope.pxd_file_loaded = True
+ # doesn't actually have any contents
+
def create_cython_scope(context):
# One could in fact probably make it a singleton,
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Dataclass.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Dataclass.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Dataclass.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Dataclass.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,740 @@
+# functions to transform a c class into a dataclass
+
+from collections import OrderedDict
+from textwrap import dedent
+import operator
+
+from . import ExprNodes
+from . import Nodes
+from . import PyrexTypes
+from . import Builtin
+from . import Naming
+from .Errors import error, warning
+from .Code import UtilityCode, TempitaUtilityCode
+from .Visitor import VisitorTransform
+from .StringEncoding import EncodedString
+from .TreeFragment import TreeFragment
+from .ParseTreeTransforms import NormalizeTree, SkipDeclarations
+from .Options import copy_inherited_directives
+
+_dataclass_loader_utilitycode = None
+
+def make_dataclasses_module_callnode(pos):
+ global _dataclass_loader_utilitycode
+ if not _dataclass_loader_utilitycode:
+ python_utility_code = UtilityCode.load_cached("Dataclasses_fallback", "Dataclasses.py")
+ python_utility_code = EncodedString(python_utility_code.impl)
+ _dataclass_loader_utilitycode = TempitaUtilityCode.load(
+ "SpecificModuleLoader", "Dataclasses.c",
+ context={'cname': "dataclasses", 'py_code': python_utility_code.as_c_string_literal()})
+ return ExprNodes.PythonCapiCallNode(
+ pos, "__Pyx_Load_dataclasses_Module",
+ PyrexTypes.CFuncType(PyrexTypes.py_object_type, []),
+ utility_code=_dataclass_loader_utilitycode,
+ args=[],
+ )
+
+_INTERNAL_DEFAULTSHOLDER_NAME = EncodedString('__pyx_dataclass_defaults')
+
+
+class RemoveAssignmentsToNames(VisitorTransform, SkipDeclarations):
+ """
+ Cython (and Python) normally treats
+
+ class A:
+ x = 1
+
+ as generating a class attribute. However for dataclasses the `= 1` should be interpreted as
+ a default value to initialize an instance attribute with.
+ This transform therefore removes the `x=1` assignment so that the class attribute isn't
+ generated, while recording what it has removed so that it can be used in the initialization.
+ """
+ def __init__(self, names):
+ super(RemoveAssignmentsToNames, self).__init__()
+ self.names = names
+ self.removed_assignments = {}
+
+ def visit_CClassNode(self, node):
+ self.visitchildren(node)
+ return node
+
+ def visit_PyClassNode(self, node):
+ return node # go no further
+
+ def visit_FuncDefNode(self, node):
+ return node # go no further
+
+ def visit_SingleAssignmentNode(self, node):
+ if node.lhs.is_name and node.lhs.name in self.names:
+ if node.lhs.name in self.removed_assignments:
+ warning(node.pos, ("Multiple assignments for '%s' in dataclass; "
+ "using most recent") % node.lhs.name, 1)
+ self.removed_assignments[node.lhs.name] = node.rhs
+ return []
+ return node
+
+ # I believe cascaded assignment is always a syntax error with annotations
+ # so there's no need to define visit_CascadedAssignmentNode
+
+ def visit_Node(self, node):
+ self.visitchildren(node)
+ return node
+
+
+class _MISSING_TYPE(object):
+ pass
+MISSING = _MISSING_TYPE()
+
+
+class Field(object):
+ """
+ Field is based on the dataclasses.field class from the standard library module.
+ It is used internally during the generation of Cython dataclasses to keep track
+ of the settings for individual attributes.
+
+ Attributes of this class are stored as nodes so they can be used in code construction
+ more readily (i.e. we store BoolNode rather than bool)
+ """
+ default = MISSING
+ default_factory = MISSING
+ private = False
+
+ literal_keys = ("repr", "hash", "init", "compare", "metadata")
+
+ # default values are defined by the CPython dataclasses.field
+ def __init__(self, pos, default=MISSING, default_factory=MISSING,
+ repr=None, hash=None, init=None,
+ compare=None, metadata=None,
+ is_initvar=False, is_classvar=False,
+ **additional_kwds):
+ if default is not MISSING:
+ self.default = default
+ if default_factory is not MISSING:
+ self.default_factory = default_factory
+ self.repr = repr or ExprNodes.BoolNode(pos, value=True)
+ self.hash = hash or ExprNodes.NoneNode(pos)
+ self.init = init or ExprNodes.BoolNode(pos, value=True)
+ self.compare = compare or ExprNodes.BoolNode(pos, value=True)
+ self.metadata = metadata or ExprNodes.NoneNode(pos)
+ self.is_initvar = is_initvar
+ self.is_classvar = is_classvar
+
+ for k, v in additional_kwds.items():
+ # There should not be any additional keywords!
+ error(v.pos, "cython.dataclasses.field() got an unexpected keyword argument '%s'" % k)
+
+ for field_name in self.literal_keys:
+ field_value = getattr(self, field_name)
+ if not field_value.is_literal:
+ error(field_value.pos,
+ "cython.dataclasses.field parameter '%s' must be a literal value" % field_name)
+
+ def iterate_record_node_arguments(self):
+ for key in (self.literal_keys + ('default', 'default_factory')):
+ value = getattr(self, key)
+ if value is not MISSING:
+ yield key, value
+
+
+def process_class_get_fields(node):
+ var_entries = node.scope.var_entries
+ # order of definition is used in the dataclass
+ var_entries = sorted(var_entries, key=operator.attrgetter('pos'))
+ var_names = [entry.name for entry in var_entries]
+
+ # don't treat `x = 1` as an assignment of a class attribute within the dataclass
+ transform = RemoveAssignmentsToNames(var_names)
+ transform(node)
+ default_value_assignments = transform.removed_assignments
+
+ if node.base_type and node.base_type.dataclass_fields:
+ fields = node.base_type.dataclass_fields.copy()
+ else:
+ fields = OrderedDict()
+
+ for entry in var_entries:
+ name = entry.name
+ is_initvar = (entry.type.python_type_constructor_name == "dataclasses.InitVar")
+ # TODO - classvars aren't included in "var_entries" so are missed here
+ # and thus this code is never triggered
+ is_classvar = (entry.type.python_type_constructor_name == "typing.ClassVar")
+ if is_initvar or is_classvar:
+ entry.type = entry.type.resolve() # no longer need the special type
+ if name in default_value_assignments:
+ assignment = default_value_assignments[name]
+ if (isinstance(assignment, ExprNodes.CallNode)
+ and assignment.function.as_cython_attribute() == "dataclasses.field"):
+ # I believe most of this is well-enforced when it's treated as a directive
+ # but it doesn't hurt to make sure
+ if (not isinstance(assignment, ExprNodes.GeneralCallNode)
+ or not isinstance(assignment.positional_args, ExprNodes.TupleNode)
+ or assignment.positional_args.args
+ or not isinstance(assignment.keyword_args, ExprNodes.DictNode)):
+ error(assignment.pos, "Call to 'cython.dataclasses.field' must only consist "
+ "of compile-time keyword arguments")
+ continue
+ keyword_args = assignment.keyword_args.as_python_dict()
+ if 'default' in keyword_args and 'default_factory' in keyword_args:
+ error(assignment.pos, "cannot specify both default and default_factory")
+ continue
+ field = Field(node.pos, **keyword_args)
+ else:
+ if isinstance(assignment, ExprNodes.CallNode):
+ func = assignment.function
+ if ((func.is_name and func.name == "field")
+ or (func.is_attribute and func.attribute == "field")):
+ warning(assignment.pos, "Do you mean cython.dataclasses.field instead?", 1)
+ if assignment.type in [Builtin.list_type, Builtin.dict_type, Builtin.set_type]:
+ # The standard library module generates a TypeError at runtime
+ # in this situation.
+ # Error message is copied from CPython
+ error(assignment.pos, "mutable default for field {1} is not allowed: "
+ "use default_factory".format(assignment.type.name, name))
+
+ field = Field(node.pos, default=assignment)
+ else:
+ field = Field(node.pos)
+ field.is_initvar = is_initvar
+ field.is_classvar = is_classvar
+ if entry.visibility == "private":
+ field.private = True
+ fields[name] = field
+ node.entry.type.dataclass_fields = fields
+ return fields
+
+
+def handle_cclass_dataclass(node, dataclass_args, analyse_decs_transform):
+ # default argument values from https://docs.python.org/3/library/dataclasses.html
+ kwargs = dict(init=True, repr=True, eq=True,
+ order=False, unsafe_hash=False, frozen=False)
+ if dataclass_args is not None:
+ if dataclass_args[0]:
+ error(node.pos, "cython.dataclasses.dataclass takes no positional arguments")
+ for k, v in dataclass_args[1].items():
+ if k not in kwargs:
+ error(node.pos,
+ "cython.dataclasses.dataclass() got an unexpected keyword argument '%s'" % k)
+ if not isinstance(v, ExprNodes.BoolNode):
+ error(node.pos,
+ "Arguments passed to cython.dataclasses.dataclass must be True or False")
+ kwargs[k] = v
+
+ fields = process_class_get_fields(node)
+
+ dataclass_module = make_dataclasses_module_callnode(node.pos)
+
+ # create __dataclass_params__ attribute. I try to use the exact
+ # `_DataclassParams` class defined in the standard library module if at all possible
+ # for maximum duck-typing compatibility.
+ dataclass_params_func = ExprNodes.AttributeNode(node.pos, obj=dataclass_module,
+ attribute=EncodedString("_DataclassParams"))
+ dataclass_params_keywords = ExprNodes.DictNode.from_pairs(
+ node.pos,
+ [ (ExprNodes.IdentifierStringNode(node.pos, value=EncodedString(k)),
+ ExprNodes.BoolNode(node.pos, value=v))
+ for k, v in kwargs.items() ])
+ dataclass_params = ExprNodes.GeneralCallNode(node.pos,
+ function = dataclass_params_func,
+ positional_args = ExprNodes.TupleNode(node.pos, args=[]),
+ keyword_args = dataclass_params_keywords)
+ dataclass_params_assignment = Nodes.SingleAssignmentNode(
+ node.pos,
+ lhs = ExprNodes.NameNode(node.pos, name=EncodedString("__dataclass_params__")),
+ rhs = dataclass_params)
+
+ dataclass_fields_stats = _set_up_dataclass_fields(node, fields, dataclass_module)
+
+ stats = Nodes.StatListNode(node.pos,
+ stats=[dataclass_params_assignment] + dataclass_fields_stats)
+
+ code_lines = []
+ placeholders = {}
+ extra_stats = []
+ for cl, ph, es in [ generate_init_code(kwargs['init'], node, fields),
+ generate_repr_code(kwargs['repr'], node, fields),
+ generate_eq_code(kwargs['eq'], node, fields),
+ generate_order_code(kwargs['order'], node, fields),
+ generate_hash_code(kwargs['unsafe_hash'], kwargs['eq'], kwargs['frozen'], node, fields) ]:
+ code_lines.append(cl)
+ placeholders.update(ph)
+ extra_stats.extend(extra_stats)
+
+ code_lines = "\n".join(code_lines)
+ code_tree = TreeFragment(code_lines, level='c_class', pipeline=[NormalizeTree(node.scope)]
+ ).substitute(placeholders)
+
+ stats.stats += (code_tree.stats + extra_stats)
+
+ # turn off annotation typing, so all arguments to __init__ are accepted as
+ # generic objects and thus can accept _HAS_DEFAULT_FACTORY.
+ # Type conversion comes later
+ comp_directives = Nodes.CompilerDirectivesNode(node.pos,
+ directives=copy_inherited_directives(node.scope.directives, annotation_typing=False),
+ body=stats)
+
+ comp_directives.analyse_declarations(node.scope)
+ # probably already in this scope, but it doesn't hurt to make sure
+ analyse_decs_transform.enter_scope(node, node.scope)
+ analyse_decs_transform.visit(comp_directives)
+ analyse_decs_transform.exit_scope()
+
+ node.body.stats.append(comp_directives)
+
+
+def generate_init_code(init, node, fields):
+ """
+ All of these "generate_*_code" functions return a tuple of:
+ - code string
+ - placeholder dict (often empty)
+ - stat list (often empty)
+ which can then be combined later and processed once.
+
+ Notes on CPython generated "__init__":
+ * Implemented in `_init_fn`.
+ * The use of the `dataclasses._HAS_DEFAULT_FACTORY` sentinel value as
+ the default argument for fields that need constructing with a factory
+ function is copied from the CPython implementation. (`None` isn't
+ suitable because it could also be a value for the user to pass.)
+ There's no real reason why it needs importing from the dataclasses module
+ though - it could equally be a value generated by Cython when the module loads.
+ * seen_default and the associated error message are copied directly from Python
+ * Call to user-defined __post_init__ function (if it exists) is copied from
+ CPython.
+ """
+ if not init or node.scope.lookup_here("__init__"):
+ return "", {}, []
+ # selfname behaviour copied from the cpython module
+ selfname = "__dataclass_self__" if "self" in fields else "self"
+ args = [selfname]
+
+ placeholders = {}
+ placeholder_count = [0]
+
+ # create a temp to get _HAS_DEFAULT_FACTORY
+ dataclass_module = make_dataclasses_module_callnode(node.pos)
+ has_default_factory = ExprNodes.AttributeNode(
+ node.pos,
+ obj=dataclass_module,
+ attribute=EncodedString("_HAS_DEFAULT_FACTORY")
+ )
+
+ def get_placeholder_name():
+ while True:
+ name = "INIT_PLACEHOLDER_%d" % placeholder_count[0]
+ if (name not in placeholders
+ and name not in fields):
+ # make sure name isn't already used and doesn't
+ # conflict with a variable name (which is unlikely but possible)
+ break
+ placeholder_count[0] += 1
+ return name
+
+ default_factory_placeholder = get_placeholder_name()
+ placeholders[default_factory_placeholder] = has_default_factory
+
+ function_body_code_lines = []
+
+ seen_default = False
+ for name, field in fields.items():
+ if not field.init.value:
+ continue
+ entry = node.scope.lookup(name)
+ if entry.annotation:
+ annotation = u": %s" % entry.annotation.string.value
+ else:
+ annotation = u""
+ assignment = u''
+ if field.default is not MISSING or field.default_factory is not MISSING:
+ seen_default = True
+ if field.default_factory is not MISSING:
+ ph_name = default_factory_placeholder
+ else:
+ ph_name = get_placeholder_name()
+ placeholders[ph_name] = field.default # should be a node
+ assignment = u" = %s" % ph_name
+ elif seen_default:
+ error(entry.pos, ("non-default argument '%s' follows default argument "
+ "in dataclass __init__") % name)
+ return "", {}, []
+
+ args.append(u"%s%s%s" % (name, annotation, assignment))
+
+ if field.is_initvar:
+ continue
+ elif field.default_factory is MISSING:
+ if field.init.value:
+ function_body_code_lines.append(u" %s.%s = %s" % (selfname, name, name))
+ else:
+ ph_name = get_placeholder_name()
+ placeholders[ph_name] = field.default_factory
+ if field.init.value:
+ # close to:
+ # def __init__(self, name=_PLACEHOLDER_VALUE):
+ # self.name = name_default_factory() if name is _PLACEHOLDER_VALUE else name
+ function_body_code_lines.append(u" %s.%s = %s() if %s is %s else %s" % (
+ selfname, name, ph_name, name, default_factory_placeholder, name))
+ else:
+ # still need to use the default factory to initialize
+ function_body_code_lines.append(u" %s.%s = %s()"
+ % (selfname, name, ph_name))
+
+ args = u", ".join(args)
+ func_def = u"def __init__(%s):" % args
+
+ code_lines = [func_def] + (function_body_code_lines or ["pass"])
+
+ if node.scope.lookup("__post_init__"):
+ post_init_vars = ", ".join(name for name, field in fields.items()
+ if field.is_initvar)
+ code_lines.append(" %s.__post_init__(%s)" % (selfname, post_init_vars))
+ return u"\n".join(code_lines), placeholders, []
+
+
+def generate_repr_code(repr, node, fields):
+ """
+ The CPython implementation is just:
+ ['return self.__class__.__qualname__ + f"(' +
+ ', '.join([f"{f.name}={{self.{f.name}!r}}"
+ for f in fields]) +
+ ')"'],
+
+ The only notable difference here is self.__class__.__qualname__ -> type(self).__name__
+ which is because Cython currently supports Python 2.
+ """
+ if not repr or node.scope.lookup("__repr__"):
+ return "", {}, []
+ code_lines = ["def __repr__(self):"]
+ strs = [u"%s={self.%s!r}" % (name, name)
+ for name, field in fields.items()
+ if field.repr.value and not field.is_initvar]
+ format_string = u", ".join(strs)
+ code_lines.append(u' name = getattr(type(self), "__qualname__", type(self).__name__)')
+ code_lines.append(u" return f'{name}(%s)'" % format_string)
+ code_lines = u"\n".join(code_lines)
+
+ return code_lines, {}, []
+
+
+def generate_cmp_code(op, funcname, node, fields):
+ if node.scope.lookup_here(funcname):
+ return "", {}, []
+
+ names = [name for name, field in fields.items() if (field.compare.value and not field.is_initvar)]
+
+ if not names:
+ return "", {}, [] # no comparable types
+
+ code_lines = [
+ "def %s(self, other):" % funcname,
+ " cdef %s other_cast" % node.class_name,
+ " if isinstance(other, %s):" % node.class_name,
+ " other_cast = <%s>other" % node.class_name,
+ " else:",
+ " return NotImplemented"
+ ]
+
+ # The Python implementation of dataclasses.py does a tuple comparison
+ # (roughly):
+ # return self._attributes_to_tuple() {op} other._attributes_to_tuple()
+ #
+ # For the Cython implementation a tuple comparison isn't an option because
+ # not all attributes can be converted to Python objects and stored in a tuple
+ #
+ # TODO - better diagnostics of whether the types support comparison before
+ # generating the code. Plus, do we want to convert C structs to dicts and
+ # compare them that way (I think not, but it might be in demand)?
+ checks = []
+ for name in names:
+ checks.append("(self.%s %s other_cast.%s)" % (
+ name, op, name))
+
+ if checks:
+ code_lines.append(" return " + " and ".join(checks))
+ else:
+ if "=" in op:
+ code_lines.append(" return True") # "() == ()" is True
+ else:
+ code_lines.append(" return False")
+
+ code_lines = u"\n".join(code_lines)
+
+ return code_lines, {}, []
+
+
+def generate_eq_code(eq, node, fields):
+ if not eq:
+ return code_lines, {}, []
+ return generate_cmp_code("==", "__eq__", node, fields)
+
+
+def generate_order_code(order, node, fields):
+ if not order:
+ return "", {}, []
+ code_lines = []
+ placeholders = {}
+ stats = []
+ for op, name in [("<", "__lt__"),
+ ("<=", "__le__"),
+ (">", "__gt__"),
+ (">=", "__ge__")]:
+ res = generate_cmp_code(op, name, node, fields)
+ code_lines.append(res[0])
+ placeholders.update(res[1])
+ stats.extend(res[2])
+ return "\n".join(code_lines), placeholders, stats
+
+
+def generate_hash_code(unsafe_hash, eq, frozen, node, fields):
+ """
+ Copied from CPython implementation - the intention is to follow this as far as
+ is possible:
+ # +------------------- unsafe_hash= parameter
+ # | +----------- eq= parameter
+ # | | +--- frozen= parameter
+ # | | |
+ # v v v | | |
+ # | no | yes | <--- class has explicitly defined __hash__
+ # +=======+=======+=======+========+========+
+ # | False | False | False | | | No __eq__, use the base class __hash__
+ # +-------+-------+-------+--------+--------+
+ # | False | False | True | | | No __eq__, use the base class __hash__
+ # +-------+-------+-------+--------+--------+
+ # | False | True | False | None | | <-- the default, not hashable
+ # +-------+-------+-------+--------+--------+
+ # | False | True | True | add | | Frozen, so hashable, allows override
+ # +-------+-------+-------+--------+--------+
+ # | True | False | False | add | raise | Has no __eq__, but hashable
+ # +-------+-------+-------+--------+--------+
+ # | True | False | True | add | raise | Has no __eq__, but hashable
+ # +-------+-------+-------+--------+--------+
+ # | True | True | False | add | raise | Not frozen, but hashable
+ # +-------+-------+-------+--------+--------+
+ # | True | True | True | add | raise | Frozen, so hashable
+ # +=======+=======+=======+========+========+
+ # For boxes that are blank, __hash__ is untouched and therefore
+ # inherited from the base class. If the base is object, then
+ # id-based hashing is used.
+
+ The Python implementation creates a tuple of all the fields, then hashes them.
+ This implementation creates a tuple of all the hashes of all the fields and hashes that.
+ The reason for this slight difference is to avoid to-Python conversions for anything
+ that Cython knows how to hash directly (It doesn't look like this currently applies to
+ anything though...).
+ """
+
+ hash_entry = node.scope.lookup_here("__hash__")
+ if hash_entry:
+ # TODO ideally assignment of __hash__ to None shouldn't trigger this
+ # but difficult to get the right information here
+ if unsafe_hash:
+ # error message taken from CPython dataclasses module
+ error(node.pos, "Cannot overwrite attribute __hash__ in class %s" % node.class_name)
+ return "", {}, []
+ if not unsafe_hash:
+ if not eq:
+ return
+ if not frozen:
+ return "", {}, [Nodes.SingleAssignmentNode(
+ node.pos,
+ lhs=ExprNodes.NameNode(node.pos, name=EncodedString("__hash__")),
+ rhs=ExprNodes.NoneNode(node.pos),
+ )]
+
+ names = [
+ name for name, field in fields.items()
+ if (not field.is_initvar and
+ (field.compare.value if field.hash.value is None else field.hash.value))
+ ]
+ if not names:
+ return "", {}, [] # nothing to hash
+
+ # make a tuple of the hashes
+ tpl = u", ".join(u"hash(self.%s)" % name for name in names )
+
+ # if we're here we want to generate a hash
+ code_lines = dedent(u"""\
+ def __hash__(self):
+ return hash((%s))
+ """) % tpl
+
+ return code_lines, {}, []
+
+
+def get_field_type(pos, entry):
+ """
+ sets the .type attribute for a field
+
+ Returns the annotation if possible (since this is what the dataclasses
+ module does). If not (for example, attributes defined with cdef) then
+ it creates a string fallback.
+ """
+ if entry.annotation:
+ # Right now it doesn't look like cdef classes generate an
+ # __annotations__ dict, therefore it's safe to just return
+ # entry.annotation
+ # (TODO: remove .string if we ditch PEP563)
+ return entry.annotation.string
+ # If they do in future then we may need to look up into that
+ # to duplicating the node. The code below should do this:
+ #class_name_node = ExprNodes.NameNode(pos, name=entry.scope.name)
+ #annotations = ExprNodes.AttributeNode(
+ # pos, obj=class_name_node,
+ # attribute=EncodedString("__annotations__")
+ #)
+ #return ExprNodes.IndexNode(
+ # pos, base=annotations,
+ # index=ExprNodes.StringNode(pos, value=entry.name)
+ #)
+ else:
+ # it's slightly unclear what the best option is here - we could
+ # try to return PyType_Type. This case should only happen with
+ # attributes defined with cdef so Cython is free to make it's own
+ # decision
+ s = entry.type.declaration_code("", for_display=1)
+ return ExprNodes.StringNode(pos, value=s)
+
+
+class FieldRecordNode(ExprNodes.ExprNode):
+ """
+ __dataclass_fields__ contains a bunch of field objects recording how each field
+ of the dataclass was initialized (mainly corresponding to the arguments passed to
+ the "field" function). This node is used for the attributes of these field objects.
+
+ If possible, coerces `arg` to a Python object.
+ Otherwise, generates a sensible backup string.
+ """
+ subexprs = ['arg']
+
+ def __init__(self, pos, arg):
+ super(FieldRecordNode, self).__init__(pos, arg=arg)
+
+ def analyse_types(self, env):
+ self.arg.analyse_types(env)
+ self.type = self.arg.type
+ return self
+
+ def coerce_to_pyobject(self, env):
+ if self.arg.type.can_coerce_to_pyobject(env):
+ return self.arg.coerce_to_pyobject(env)
+ else:
+ # A string representation of the code that gave the field seems like a reasonable
+ # fallback. This'll mostly happen for "default" and "default_factory" where the
+ # type may be a C-type that can't be converted to Python.
+ return self._make_string()
+
+ def _make_string(self):
+ from .AutoDocTransforms import AnnotationWriter
+ writer = AnnotationWriter(description="Dataclass field")
+ string = writer.write(self.arg)
+ return ExprNodes.StringNode(self.pos, value=EncodedString(string))
+
+ def generate_evaluation_code(self, code):
+ return self.arg.generate_evaluation_code(code)
+
+
+def _set_up_dataclass_fields(node, fields, dataclass_module):
+ # For defaults and default_factories containing things like lambda,
+ # they're already declared in the class scope, and it creates a big
+ # problem if multiple copies are floating around in both the __init__
+ # function, and in the __dataclass_fields__ structure.
+ # Therefore, create module-level constants holding these values and
+ # pass those around instead
+ #
+ # If possible we use the `Field` class defined in the standard library
+ # module so that the information stored here is as close to a regular
+ # dataclass as is possible.
+ variables_assignment_stats = []
+ for name, field in fields.items():
+ if field.private:
+ continue # doesn't appear in the public interface
+ for attrname in [ "default", "default_factory" ]:
+ field_default = getattr(field, attrname)
+ if field_default is MISSING or field_default.is_literal or field_default.is_name:
+ # some simple cases where we don't need to set up
+ # the variable as a module-level constant
+ continue
+ global_scope = node.scope.global_scope()
+ module_field_name = global_scope.mangle(
+ global_scope.mangle(Naming.dataclass_field_default_cname, node.class_name),
+ name)
+ # create an entry in the global scope for this variable to live
+ field_node = ExprNodes.NameNode(field_default.pos, name=EncodedString(module_field_name))
+ field_node.entry = global_scope.declare_var(field_node.name, type=field_default.type or PyrexTypes.unspecified_type,
+ pos=field_default.pos, cname=field_node.name, is_cdef=1)
+ # replace the field so that future users just receive the namenode
+ setattr(field, attrname, field_node)
+
+ variables_assignment_stats.append(
+ Nodes.SingleAssignmentNode(field_default.pos, lhs=field_node, rhs=field_default))
+
+ placeholders = {}
+ field_func = ExprNodes.AttributeNode(node.pos, obj=dataclass_module,
+ attribute=EncodedString("field"))
+ dc_fields = ExprNodes.DictNode(node.pos, key_value_pairs=[])
+ dc_fields_namevalue_assignments = []
+
+ for name, field in fields.items():
+ if field.private:
+ continue # doesn't appear in the public interface
+ type_placeholder_name = "PLACEHOLDER_%s" % name
+ placeholders[type_placeholder_name] = get_field_type(
+ node.pos, node.scope.entries[name]
+ )
+
+ # defining these make the fields introspect more like a Python dataclass
+ field_type_placeholder_name = "PLACEHOLDER_FIELD_TYPE_%s" % name
+ if field.is_initvar:
+ placeholders[field_type_placeholder_name] = ExprNodes.AttributeNode(
+ node.pos, obj=dataclass_module,
+ attribute=EncodedString("_FIELD_INITVAR")
+ )
+ elif field.is_classvar:
+ # TODO - currently this isn't triggered
+ placeholders[field_type_placeholder_name] = ExprNodes.AttributeNode(
+ node.pos, obj=dataclass_module,
+ attribute=EncodedString("_FIELD_CLASSVAR")
+ )
+ else:
+ placeholders[field_type_placeholder_name] = ExprNodes.AttributeNode(
+ node.pos, obj=dataclass_module,
+ attribute=EncodedString("_FIELD")
+ )
+
+ dc_field_keywords = ExprNodes.DictNode.from_pairs(
+ node.pos,
+ [(ExprNodes.IdentifierStringNode(node.pos, value=EncodedString(k)),
+ FieldRecordNode(node.pos, arg=v))
+ for k, v in field.iterate_record_node_arguments()]
+
+ )
+ dc_field_call = ExprNodes.GeneralCallNode(
+ node.pos, function = field_func,
+ positional_args = ExprNodes.TupleNode(node.pos, args=[]),
+ keyword_args = dc_field_keywords)
+ dc_fields.key_value_pairs.append(
+ ExprNodes.DictItemNode(
+ node.pos,
+ key=ExprNodes.IdentifierStringNode(node.pos, value=EncodedString(name)),
+ value=dc_field_call))
+ dc_fields_namevalue_assignments.append(
+ dedent(u"""\
+ __dataclass_fields__[{0!r}].name = {0!r}
+ __dataclass_fields__[{0!r}].type = {1}
+ __dataclass_fields__[{0!r}]._field_type = {2}
+ """).format(name, type_placeholder_name, field_type_placeholder_name))
+
+ dataclass_fields_assignment = \
+ Nodes.SingleAssignmentNode(node.pos,
+ lhs = ExprNodes.NameNode(node.pos,
+ name=EncodedString("__dataclass_fields__")),
+ rhs = dc_fields)
+
+ dc_fields_namevalue_assignments = u"\n".join(dc_fields_namevalue_assignments)
+ dc_fields_namevalue_assignments = TreeFragment(dc_fields_namevalue_assignments,
+ level="c_class",
+ pipeline=[NormalizeTree(None)])
+ dc_fields_namevalue_assignments = dc_fields_namevalue_assignments.substitute(placeholders)
+
+ return (variables_assignment_stats
+ + [dataclass_fields_assignment]
+ + dc_fields_namevalue_assignments.stats)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Errors.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Errors.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Errors.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Errors.py 2022-03-24 10:16:46.000000000 +0000
@@ -10,6 +10,14 @@
any_string_type = (bytes, str)
import sys
+from contextlib import contextmanager
+
+try:
+ from threading import local as _threadlocal
+except ImportError:
+ class _threadlocal(object): pass
+
+threadlocal = _threadlocal()
from ..Utils import open_new_file
from . import DebugFlags
@@ -23,6 +31,8 @@
class PyrexWarning(Exception):
pass
+class CannotSpecialize(PyrexError):
+ pass
def context(position):
source = position[0]
@@ -59,11 +69,9 @@
self.message_only = message
self.formatted_message = format_error(message, position)
self.reported = False
- # Deprecated and withdrawn in 2.6:
- # self.message = message
Exception.__init__(self, self.formatted_message)
# Python Exception subclass pickling is broken,
- # see http://bugs.python.org/issue1692335
+ # see https://bugs.python.org/issue1692335
self.args = (position, message)
def __str__(self):
@@ -73,8 +81,6 @@
def __init__(self, position = None, message = ""):
self.position = position
- # Deprecated and withdrawn in 2.6:
- # self.message = message
Exception.__init__(self, format_position(position) + message)
class InternalError(Exception):
@@ -113,7 +119,7 @@
message += u'%s: %s' % (cause.__class__.__name__, cause)
CompileError.__init__(self, pos, message)
# Python Exception subclass pickling is broken,
- # see http://bugs.python.org/issue1692335
+ # see https://bugs.python.org/issue1692335
self.args = (pos, context, message, cause, stacktrace)
class NoElementTreeInstalledException(PyrexError):
@@ -121,35 +127,29 @@
implementation was found
"""
-listing_file = None
-num_errors = 0
-echo_file = None
-
-def open_listing_file(path, echo_to_stderr = 1):
+def open_listing_file(path, echo_to_stderr=True):
# Begin a new error listing. If path is None, no file
# is opened, the error counter is just reset.
- global listing_file, num_errors, echo_file
if path is not None:
- listing_file = open_new_file(path)
+ threadlocal.cython_errors_listing_file = open_new_file(path)
else:
- listing_file = None
+ threadlocal.cython_errors_listing_file = None
if echo_to_stderr:
- echo_file = sys.stderr
+ threadlocal.cython_errors_echo_file = sys.stderr
else:
- echo_file = None
- num_errors = 0
+ threadlocal.cython_errors_echo_file = None
+ threadlocal.cython_errors_count = 0
def close_listing_file():
- global listing_file
- if listing_file:
- listing_file.close()
- listing_file = None
-
-def report_error(err):
- if error_stack:
+ if threadlocal.cython_errors_listing_file:
+ threadlocal.cython_errors_listing_file.close()
+ threadlocal.cython_errors_listing_file = None
+
+def report_error(err, use_stack=True):
+ error_stack = threadlocal.cython_errors_stack
+ if error_stack and use_stack:
error_stack[-1].append(err)
else:
- global num_errors
# See Main.py for why dual reporting occurs. Quick fix for now.
if err.reported: return
err.reported = True
@@ -158,41 +158,50 @@
# Python <= 2.5 does this for non-ASCII Unicode exceptions
line = format_error(getattr(err, 'message_only', "[unprintable exception message]"),
getattr(err, 'position', None)) + u'\n'
+ listing_file = threadlocal.cython_errors_listing_file
if listing_file:
try: listing_file.write(line)
except UnicodeEncodeError:
listing_file.write(line.encode('ASCII', 'replace'))
+ echo_file = threadlocal.cython_errors_echo_file
if echo_file:
try: echo_file.write(line)
except UnicodeEncodeError:
echo_file.write(line.encode('ASCII', 'replace'))
- num_errors += 1
+ threadlocal.cython_errors_count += 1
if Options.fast_fail:
raise AbortError("fatal errors")
-
def error(position, message):
#print("Errors.error:", repr(position), repr(message)) ###
if position is None:
raise InternalError(message)
err = CompileError(position, message)
- if DebugFlags.debug_exception_on_error: raise Exception(err) # debug
+ if DebugFlags.debug_exception_on_error: raise Exception(err) # debug
report_error(err)
return err
-LEVEL = 1 # warn about all errors level 1 or higher
+LEVEL = 1 # warn about all errors level 1 or higher
+
+def _write_file_encode(file, line):
+ try:
+ file.write(line)
+ except UnicodeEncodeError:
+ file.write(line.encode('ascii', 'replace'))
def message(position, message, level=1):
if level < LEVEL:
return
warn = CompileWarning(position, message)
- line = "note: %s\n" % warn
+ line = u"note: %s\n" % warn
+ listing_file = threadlocal.cython_errors_listing_file
if listing_file:
- listing_file.write(line)
+ _write_file_encode(listing_file, line)
+ echo_file = threadlocal.cython_errors_echo_file
if echo_file:
- echo_file.write(line)
+ _write_file_encode(echo_file, line)
return warn
@@ -202,48 +211,76 @@
if Options.warning_errors and position:
return error(position, message)
warn = CompileWarning(position, message)
- line = "warning: %s\n" % warn
+ line = u"warning: %s\n" % warn
+ listing_file = threadlocal.cython_errors_listing_file
if listing_file:
- listing_file.write(line)
+ _write_file_encode(listing_file, line)
+ echo_file = threadlocal.cython_errors_echo_file
if echo_file:
- echo_file.write(line)
+ _write_file_encode(echo_file, line)
return warn
-_warn_once_seen = {}
def warn_once(position, message, level=0):
- if level < LEVEL or message in _warn_once_seen:
+ if level < LEVEL:
+ return
+ warn_once_seen = threadlocal.cython_errors_warn_once_seen
+ if message in warn_once_seen:
return
warn = CompileWarning(position, message)
- line = "warning: %s\n" % warn
+ line = u"warning: %s\n" % warn
+ listing_file = threadlocal.cython_errors_listing_file
if listing_file:
- listing_file.write(line)
+ _write_file_encode(listing_file, line)
+ echo_file = threadlocal.cython_errors_echo_file
if echo_file:
- echo_file.write(line)
- _warn_once_seen[message] = True
+ _write_file_encode(echo_file, line)
+ warn_once_seen[message] = True
return warn
# These functions can be used to momentarily suppress errors.
-error_stack = []
-
def hold_errors():
- error_stack.append([])
+ errors = []
+ threadlocal.cython_errors_stack.append(errors)
+ return errors
+
def release_errors(ignore=False):
- held_errors = error_stack.pop()
+ held_errors = threadlocal.cython_errors_stack.pop()
if not ignore:
for err in held_errors:
report_error(err)
+
def held_errors():
- return error_stack[-1]
+ return threadlocal.cython_errors_stack[-1]
-# this module needs a redesign to support parallel cythonisation, but
-# for now, the following works at least in sequential compiler runs
+# same as context manager:
+
+@contextmanager
+def local_errors(ignore=False):
+ errors = hold_errors()
+ try:
+ yield errors
+ finally:
+ release_errors(ignore=ignore)
+
+
+# Keep all global state in thread local storage to support parallel cythonisation in distutils.
+
+def init_thread():
+ threadlocal.cython_errors_count = 0
+ threadlocal.cython_errors_listing_file = None
+ threadlocal.cython_errors_echo_file = None
+ threadlocal.cython_errors_warn_once_seen = set()
+ threadlocal.cython_errors_stack = []
def reset():
- _warn_once_seen.clear()
- del error_stack[:]
+ threadlocal.cython_errors_warn_once_seen.clear()
+ del threadlocal.cython_errors_stack[:]
+
+def get_errors_count():
+ return threadlocal.cython_errors_count
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/ExprNodes.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/ExprNodes.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/ExprNodes.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/ExprNodes.py 2022-03-24 10:16:46.000000000 +0000
@@ -7,7 +7,7 @@
import cython
cython.declare(error=object, warning=object, warn_once=object, InternalError=object,
CompileError=object, UtilityCode=object, TempitaUtilityCode=object,
- StringEncoding=object, operator=object,
+ StringEncoding=object, operator=object, local_errors=object, report_error=object,
Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object,
list_type=object, tuple_type=object, set_type=object, dict_type=object,
unicode_type=object, str_type=object, bytes_type=object, type_type=object,
@@ -16,32 +16,42 @@
bytearray_type=object, slice_type=object, _py_int_types=object,
IS_PYTHON3=cython.bint)
+import re
import sys
import copy
import os.path
import operator
-from .Errors import error, warning, warn_once, InternalError, CompileError
-from .Errors import hold_errors, release_errors, held_errors, report_error
+from .Errors import (
+ error, warning, InternalError, CompileError, report_error, local_errors,
+ CannotSpecialize)
from .Code import UtilityCode, TempitaUtilityCode
from . import StringEncoding
from . import Naming
from . import Nodes
-from .Nodes import Node, utility_code_for_imports
+from .Nodes import Node, utility_code_for_imports, SingleAssignmentNode
from . import PyrexTypes
-from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
+from .PyrexTypes import py_object_type, typecast, error_type, \
unspecified_type
from . import TypeSlots
-from .Builtin import list_type, tuple_type, set_type, dict_type, type_type, \
- unicode_type, str_type, bytes_type, bytearray_type, basestring_type, slice_type
+from .Builtin import (
+ list_type, tuple_type, set_type, dict_type, type_type,
+ unicode_type, str_type, bytes_type, bytearray_type, basestring_type,
+ slice_type, long_type,
+)
from . import Builtin
from . import Symtab
from .. import Utils
from .Annotate import AnnotationItem
from . import Future
from ..Debugging import print_call_chain
-from .DebugFlags import debug_disposal_code, debug_temp_alloc, \
- debug_coercion
+from .DebugFlags import debug_disposal_code, debug_coercion
+
+from .Pythran import (to_pythran, is_pythran_supported_type, is_pythran_supported_operation_type,
+ is_pythran_expr, pythran_func_type, pythran_binop_type, pythran_unaryop_type, has_np_pythran,
+ pythran_indexing_code, pythran_indexing_type, is_pythran_supported_node_or_none, pythran_type,
+ pythran_is_numpy_func_supported, pythran_get_func_include_file, pythran_functor)
+from .PyrexTypes import PythranExpr
try:
from __builtin__ import basestring
@@ -113,7 +123,6 @@
"Cannot convert 'char*' to unicode implicitly, decoding required"),
}
-
def find_coercion_error(type_tuple, default, env):
err = coercion_error_dict.get(type_tuple)
if err is None:
@@ -177,25 +186,84 @@
else:
return item.infer_type(env)
# if we're lucky, all items have the same type
- item_types = set([item.infer_type(env) for item in seq_node.args])
+ item_types = {item.infer_type(env) for item in seq_node.args}
if len(item_types) == 1:
return item_types.pop()
return None
+
+def make_dedup_key(outer_type, item_nodes):
+ """
+ Recursively generate a deduplication key from a sequence of values.
+ Includes Cython node types to work around the fact that (1, 2.0) == (1.0, 2), for example.
+
+ @param outer_type: The type of the outer container.
+ @param item_nodes: A sequence of constant nodes that will be traversed recursively.
+ @return: A tuple that can be used as a dict key for deduplication.
+ """
+ item_keys = [
+ (py_object_type, None, type(None)) if node is None
+ # For sequences and their "mult_factor", see TupleNode.
+ else make_dedup_key(node.type, [node.mult_factor if node.is_literal else None] + node.args) if node.is_sequence_constructor
+ else make_dedup_key(node.type, (node.start, node.stop, node.step)) if node.is_slice
+ # For constants, look at the Python value type if we don't know the concrete Cython type.
+ else (node.type, node.constant_result,
+ type(node.constant_result) if node.type is py_object_type else None) if node.has_constant_result()
+ # IdentifierStringNode doesn't usually have a "constant_result" set because:
+ # 1. it doesn't usually have unicode_value
+ # 2. it's often created later in the compilation process after ConstantFolding
+ # but should be cacheable
+ else (node.type, node.value, node.unicode_value, "IdentifierStringNode") if isinstance(node, IdentifierStringNode)
+ else None # something we cannot handle => short-circuit below
+ for node in item_nodes
+ ]
+ if None in item_keys:
+ return None
+ return outer_type, tuple(item_keys)
+
+
+# Returns a block of code to translate the exception,
+# plus a boolean indicating whether to check for Python exceptions.
def get_exception_handler(exception_value):
if exception_value is None:
- return "__Pyx_CppExn2PyErr();"
+ return "__Pyx_CppExn2PyErr();", False
+ elif (exception_value.type == PyrexTypes.c_char_type
+ and exception_value.value == '*'):
+ return "__Pyx_CppExn2PyErr();", True
elif exception_value.type.is_pyobject:
- return 'try { throw; } catch(const std::exception& exn) { PyErr_SetString(%s, exn.what()); } catch(...) { PyErr_SetNone(%s); }' % (
- exception_value.entry.cname,
- exception_value.entry.cname)
+ return (
+ 'try { throw; } catch(const std::exception& exn) {'
+ 'PyErr_SetString(%s, exn.what());'
+ '} catch(...) { PyErr_SetNone(%s); }' % (
+ exception_value.entry.cname,
+ exception_value.entry.cname),
+ False)
else:
- return '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.");' % exception_value.entry.cname
+ return (
+ '%s(); if (!PyErr_Occurred())'
+ 'PyErr_SetString(PyExc_RuntimeError, '
+ '"Error converting c++ exception.");' % (
+ exception_value.entry.cname),
+ False)
+
+
+def maybe_check_py_error(code, check_py_exception, pos, nogil):
+ if check_py_exception:
+ if nogil:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("ErrOccurredWithGIL", "Exceptions.c"))
+ code.putln(code.error_goto_if("__Pyx_ErrOccurredWithGIL()", pos))
+ else:
+ code.putln(code.error_goto_if("PyErr_Occurred()", pos))
-def translate_cpp_exception(code, pos, inside, exception_value, nogil):
- raise_py_exception = get_exception_handler(exception_value)
+
+def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil):
+ raise_py_exception, check_py_exception = get_exception_handler(exception_value)
code.putln("try {")
code.putln("%s" % inside)
+ if py_result:
+ code.putln(code.error_goto_if_null(py_result, pos))
+ maybe_check_py_error(code, check_py_exception, pos, nogil)
code.putln("} catch(...) {")
if nogil:
code.put_ensure_gil(declare_gilstate=True)
@@ -205,16 +273,32 @@
code.putln(code.error_goto(pos))
code.putln("}")
+def needs_cpp_exception_conversion(node):
+ assert node.exception_check == "+"
+ if node.exception_value is None:
+ return True
+ # exception_value can be a NameNode
+ # (in which case it's used as a handler function and no conversion is needed)
+ if node.exception_value.is_name:
+ return False
+ # or a CharNode with a value of "*"
+ if isinstance(node.exception_value, CharNode) and node.exception_value.value == "*":
+ return True
+ # Most other const-nodes are disallowed after "+" by the parser
+ return False
+
+
# Used to handle the case where an lvalue expression and an overloaded assignment
# both have an exception declaration.
-def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code,
- lhs_exc_val, assign_exc_val, nogil):
- handle_lhs_exc = get_exception_handler(lhs_exc_val)
- handle_assignment_exc = get_exception_handler(assign_exc_val)
+def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code, lhs_exc_val, assign_exc_val, nogil):
+ handle_lhs_exc, lhc_check_py_exc = get_exception_handler(lhs_exc_val)
+ handle_assignment_exc, assignment_check_py_exc = get_exception_handler(assign_exc_val)
code.putln("try {")
code.putln(lhs_type.declaration_code("__pyx_local_lvalue = %s;" % lhs_code))
+ maybe_check_py_error(code, lhc_check_py_exc, pos, nogil)
code.putln("try {")
code.putln("__pyx_local_lvalue = %s;" % rhs_code)
+ maybe_check_py_error(code, assignment_check_py_exc, pos, nogil)
# Catch any exception from the overloaded assignment.
code.putln("} catch(...) {")
if nogil:
@@ -249,14 +333,18 @@
# Cached result of subexpr_nodes()
# use_managed_ref boolean use ref-counted temps/assignments/etc.
# result_is_used boolean indicates that the result will be dropped and the
+ # is_numpy_attribute boolean Is a Numpy module attribute
# result_code/temp_result can safely be set to None
+ # annotation ExprNode or None PEP526 annotation for names or expressions
result_ctype = None
type = None
+ annotation = None
temp_code = None
- old_temp = None # error checker for multiple frees etc.
- use_managed_ref = True # can be set by optimisation transforms
+ old_temp = None # error checker for multiple frees etc.
+ use_managed_ref = True # can be set by optimisation transforms
result_is_used = True
+ is_numpy_attribute = False
# The Analyse Expressions phase for expressions is split
# into two sub-phases:
@@ -387,6 +475,7 @@
saved_subexpr_nodes = None
is_temp = False
+ has_temp_moved = False # if True then attempting to do anything but free the temp is invalid
is_target = False
is_starred = False
@@ -394,11 +483,13 @@
child_attrs = property(fget=operator.attrgetter('subexprs'))
+ def analyse_annotations(self, env):
+ pass
+
def not_implemented(self, method_name):
- print_call_chain(method_name, "not implemented") ###
+ print_call_chain(method_name, "not implemented")
raise InternalError(
- "%s.%s not implemented" %
- (self.__class__.__name__, method_name))
+ "%s.%s not implemented" % (self.__class__.__name__, method_name))
def is_lvalue(self):
return 0
@@ -437,6 +528,29 @@
else:
return self.calculate_result_code()
+ def _make_move_result_rhs(self, result, optional=False):
+ if optional and not (self.is_temp and self.type.is_cpp_class and not self.type.is_reference):
+ return result
+ self.has_temp_moved = True
+ return "{}({})".format("__PYX_STD_MOVE_IF_SUPPORTED" if optional else "std::move", result)
+
+ def move_result_rhs(self):
+ return self._make_move_result_rhs(self.result(), optional=True)
+
+ def move_result_rhs_as(self, type):
+ result = self.result_as(type)
+ if not (type.is_reference or type.needs_refcounting):
+ requires_move = type.is_rvalue_reference and self.is_temp
+ result = self._make_move_result_rhs(result, optional=not requires_move)
+ return result
+
+ def pythran_result(self, type_=None):
+ if is_pythran_supported_node_or_none(self):
+ return to_pythran(self)
+
+ assert(type_ is not None)
+ return to_pythran(self, type_)
+
def is_c_result_required(self):
"""
Subtypes may return False here if result temp allocation can be skipped.
@@ -501,6 +615,9 @@
def analyse_target_declaration(self, env):
error(self.pos, "Cannot assign to or delete this")
+ def analyse_assignment_expression_target_declaration(self, env):
+ error(self.pos, "Cannot use anything except a name in an assignment expression")
+
# ------------- Expression Analysis ----------------
def analyse_const_expression(self, env):
@@ -546,7 +663,7 @@
def type_dependencies(self, env):
# Returns the list of entries whose types must be determined
# before the type of self can be inferred.
- if hasattr(self, 'type') and self.type is not None:
+ if getattr(self, 'type', None) is not None:
return ()
return sum([node.type_dependencies(env) for node in self.subexpr_nodes()], ())
@@ -555,12 +672,13 @@
# Differs from analyse_types as it avoids unnecessary
# analysis of subexpressions, but can assume everything
# in self.type_dependencies() has been resolved.
- if hasattr(self, 'type') and self.type is not None:
- return self.type
- elif hasattr(self, 'entry') and self.entry is not None:
- return self.entry.type
- else:
- self.not_implemented("infer_type")
+ type = getattr(self, 'type', None)
+ if type is not None:
+ return type
+ entry = getattr(self, 'entry', None)
+ if entry is not None:
+ return entry.type
+ self.not_implemented("infer_type")
def nonlocally_immutable(self):
# Returns whether this variable is a safe reference, i.e.
@@ -587,6 +705,19 @@
# type, return that type, else None.
return None
+ def analyse_as_specialized_type(self, env):
+ type = self.analyse_as_type(env)
+ if type and type.is_fused and env.fused_to_specific:
+ # while it would be nice to test "if entry.type in env.fused_to_specific"
+ # rather than try/catch this doesn't work reliably (mainly for nested fused types)
+ try:
+ return type.specialize(env.fused_to_specific)
+ except KeyError:
+ pass
+ if type and type.is_fused:
+ error(self.pos, "Type is not specific")
+ return type
+
def analyse_as_extension_type(self, env):
# If this node can be interpreted as a reference to an
# extension type or builtin type, return its type, else None.
@@ -678,19 +809,20 @@
def make_owned_reference(self, code):
"""
- If result is a pyobject, make sure we own a reference to it.
+ Make sure we own a reference to result.
If the result is in a temp, it is already a new reference.
"""
- if self.type.is_pyobject and not self.result_in_temp():
+ if not self.result_in_temp():
code.put_incref(self.result(), self.ctype())
def make_owned_memoryviewslice(self, code):
"""
Make sure we own the reference to this memoryview slice.
"""
+ # TODO ideally this would be shared with "make_owned_reference"
if not self.result_in_temp():
- code.put_incref_memoryviewslice(self.result(),
- have_gil=self.in_nogil_context)
+ code.put_incref_memoryviewslice(self.result(), self.type,
+ have_gil=not self.in_nogil_context)
def generate_evaluation_code(self, code):
# Generate code to evaluate this node and
@@ -717,19 +849,17 @@
self.not_implemented("generate_result_code")
def generate_disposal_code(self, code):
+ if self.has_temp_moved:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("MoveIfSupported", "CppSupport.cpp"))
if self.is_temp:
if self.type.is_string or self.type.is_pyunicode_ptr:
# postponed from self.generate_evaluation_code()
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
if self.result():
- if self.type.is_pyobject:
- code.put_decref_clear(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=not self.in_nogil_context)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
+ code.put_decref_clear(self.result(), self.ctype(),
+ have_gil=not self.in_nogil_context)
else:
# Already done if self.is_temp
self.generate_subexpr_disposal_code(code)
@@ -751,11 +881,15 @@
elif self.type.is_memoryviewslice:
code.putln("%s.memview = NULL;" % self.result())
code.putln("%s.data = NULL;" % self.result())
+
+ if self.has_temp_moved:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("MoveIfSupported", "CppSupport.cpp"))
else:
self.generate_subexpr_disposal_code(code)
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
# Stub method for nodes which are not legal as
# the LHS of an assignment. An error will have
# been reported earlier.
@@ -781,6 +915,32 @@
def generate_function_definitions(self, env, code):
pass
+ # ----Generation of small bits of reference counting --
+
+ def generate_decref_set(self, code, rhs):
+ code.put_decref_set(self.result(), self.ctype(), rhs)
+
+ def generate_xdecref_set(self, code, rhs):
+ code.put_xdecref_set(self.result(), self.ctype(), rhs)
+
+ def generate_gotref(self, code, handle_null=False,
+ maybe_null_extra_check=True):
+ if not (handle_null and self.cf_is_null):
+ if (handle_null and self.cf_maybe_null
+ and maybe_null_extra_check):
+ self.generate_xgotref(code)
+ else:
+ code.put_gotref(self.result(), self.ctype())
+
+ def generate_xgotref(self, code):
+ code.put_xgotref(self.result(), self.ctype())
+
+ def generate_giveref(self, code):
+ code.put_giveref(self.result(), self.ctype())
+
+ def generate_xgiveref(self, code):
+ code.put_xgiveref(self.result(), self.ctype())
+
# ---------------- Annotation ---------------------
def annotate(self, code):
@@ -815,8 +975,8 @@
if used_as_reference and not src_type.is_reference:
dst_type = dst_type.ref_base_type
- if src_type.is_const:
- src_type = src_type.const_base_type
+ if src_type.is_cv_qualified:
+ src_type = src_type.cv_base_type
if src_type.is_fused or dst_type.is_fused:
# See if we are coercing a fused function to a pointer to a
@@ -835,6 +995,9 @@
if src_type.is_fused:
error(self.pos, "Type is not specialized")
+ elif src_type.is_null_ptr and dst_type.is_ptr:
+ # NULL can be implicitly cast to any pointer type
+ return self
else:
error(self.pos, "Cannot coerce to a type that is not specialized")
@@ -856,16 +1019,19 @@
elif not src_type.is_error:
error(self.pos,
"Cannot convert '%s' to memoryviewslice" % (src_type,))
- elif not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast,
- copying=self.is_memview_copy_assignment):
- if src.type.dtype.same_as(dst_type.dtype):
- msg = "Memoryview '%s' not conformable to memoryview '%s'."
- tup = src.type, dst_type
- else:
- msg = "Different base types for memoryviews (%s, %s)"
- tup = src.type.dtype, dst_type.dtype
+ else:
+ if src.type.writable_needed:
+ dst_type.writable_needed = True
+ if not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast,
+ copying=self.is_memview_copy_assignment):
+ if src.type.dtype.same_as(dst_type.dtype):
+ msg = "Memoryview '%s' not conformable to memoryview '%s'."
+ tup = src.type, dst_type
+ else:
+ msg = "Different base types for memoryviews (%s, %s)"
+ tup = src.type.dtype, dst_type.dtype
- error(self.pos, msg % tup)
+ error(self.pos, msg % tup)
elif dst_type.is_pyobject:
if not src.type.is_pyobject:
@@ -876,6 +1042,16 @@
if not src.type.subtype_of(dst_type):
if src.constant_result is not None:
src = PyTypeTestNode(src, dst_type, env)
+ elif is_pythran_expr(dst_type) and is_pythran_supported_type(src.type):
+ # We let the compiler decide whether this is valid
+ return src
+ elif is_pythran_expr(src.type):
+ if is_pythran_supported_type(dst_type):
+ # Match the case were a pythran expr is assigned to a value, or vice versa.
+ # We let the C++ compiler decide whether this is valid or not!
+ return src
+ # Else, we need to convert the Pythran expression to a Python object
+ src = CoerceToPyTypeNode(src, env, type=dst_type)
elif src.type.is_pyobject:
if used_as_reference and dst_type.is_cpp_class:
warning(
@@ -886,7 +1062,8 @@
and src_type != dst_type
and dst_type.assignable_from(src_type)):
src = CoerceToComplexNode(src, dst_type, env)
- else: # neither src nor dst are py types
+ else:
+ # neither src nor dst are py types
# Added the string comparison, since for c types that
# is enough, but Cython gets confused when the types are
# in different pxi files.
@@ -928,11 +1105,11 @@
return self
elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
return CoerceToBooleanNode(self, env)
- elif type.is_cpp_class:
+ elif type.is_cpp_class and type.scope and type.scope.lookup("operator bool"):
return SimpleCallNode(
self.pos,
function=AttributeNode(
- self.pos, obj=self, attribute='operator bool'),
+ self.pos, obj=self, attribute=StringEncoding.EncodedString('operator bool')),
args=[]).analyse_types(env)
elif type.is_ctuple:
bool_value = len(type.components) == 0
@@ -1006,6 +1183,15 @@
kwargs[attr_name] = value
return cls(node.pos, **kwargs)
+ def get_known_standard_library_import(self):
+ """
+ Gets the module.path that this node was imported from.
+
+ Many nodes do not have one, or it is ambiguous, in which case
+ this function returns a false value.
+ """
+ return None
+
class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have
@@ -1024,6 +1210,7 @@
is_literal = 1
type = py_object_type
+ nogil_check = None
def is_simple(self):
return 1
@@ -1049,14 +1236,18 @@
constant_result = None
- nogil_check = None
-
def compile_time_value(self, denv):
return None
def may_be_none(self):
return True
+ def coerce_to(self, dst_type, env):
+ if not (dst_type.is_pyobject or dst_type.is_memoryviewslice or dst_type.is_error):
+ # Catch this error early and loudly.
+ error(self.pos, "Cannot assign None to %s" % dst_type)
+ return super(NoneNode, self).coerce_to(dst_type, env)
+
class EllipsisNode(PyConstNode):
# '...' in a subscript list.
@@ -1114,11 +1305,15 @@
def calculate_result_code(self):
if self.type.is_pyobject:
- return self.value and 'Py_True' or 'Py_False'
+ return 'Py_True' if self.value else 'Py_False'
else:
return str(int(self.value))
def coerce_to(self, dst_type, env):
+ if dst_type == self.type:
+ return self
+ if dst_type is py_object_type and self.type is Builtin.bool_type:
+ return self
if dst_type.is_pyobject and self.type.is_int:
return BoolNode(
self.pos, value=self.value,
@@ -1162,7 +1357,7 @@
unsigned = ""
longness = ""
- is_c_literal = None # unknown
+ is_c_literal = None # unknown
def __init__(self, pos, **kwds):
ExprNode.__init__(self, pos, **kwds)
@@ -1288,7 +1483,6 @@
def compile_time_value(self, denv):
return Utils.str_to_number(self.value)
-
class FloatNode(ConstNode):
type = PyrexTypes.c_double_type
@@ -1339,19 +1533,24 @@
type = PyrexTypes.parse_basic_type(name)
if type is not None:
return type
- hold_errors()
+
+ global_entry = env.global_scope().lookup(name)
+ if global_entry and global_entry.is_type and global_entry.type:
+ return global_entry.type
+
from .TreeFragment import TreeFragment
- pos = (pos[0], pos[1], pos[2]-7)
- try:
- declaration = TreeFragment(u"sizeof(%s)" % name, name=pos[0].filename, initial_pos=pos)
- except CompileError:
- sizeof_node = None
- else:
- sizeof_node = declaration.root.stats[0].expr
- sizeof_node = sizeof_node.analyse_types(env)
- release_errors(ignore=True)
- if isinstance(sizeof_node, SizeofTypeNode):
- return sizeof_node.arg_type
+ with local_errors(ignore=True):
+ pos = (pos[0], pos[1], pos[2]-7)
+ try:
+ declaration = TreeFragment(u"sizeof(%s)" % name, name=pos[0].filename, initial_pos=pos)
+ except CompileError:
+ pass
+ else:
+ sizeof_node = declaration.root.stats[0].expr
+ if isinstance(sizeof_node, SizeofTypeNode):
+ sizeof_node = sizeof_node.analyse_types(env)
+ if isinstance(sizeof_node, SizeofTypeNode):
+ return sizeof_node.arg_type
return None
@@ -1405,7 +1604,7 @@
node.type = Builtin.bytes_type
else:
self.check_for_coercion_error(dst_type, env, fail=True)
- return node
+ return node
elif dst_type in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
node.type = dst_type
return node
@@ -1414,8 +1613,10 @@
else PyrexTypes.c_char_ptr_type)
return CastNode(node, dst_type)
elif dst_type.assignable_from(PyrexTypes.c_char_ptr_type):
- node.type = dst_type
- return node
+ # Exclude the case of passing a C string literal into a non-const C++ string.
+ if not dst_type.is_cpp_class or dst_type.is_const:
+ node.type = dst_type
+ return node
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
@@ -1435,7 +1636,7 @@
self.result_code = result
def get_constant_c_result_code(self):
- return None # FIXME
+ return None # FIXME
def calculate_result_code(self):
return self.result_code
@@ -1490,12 +1691,9 @@
if dst_type.is_string and self.bytes_value is not None:
# special case: '-3' enforced unicode literal used in a
# C char* context
- return BytesNode(self.pos, value=self.bytes_value
- ).coerce_to(dst_type, env)
+ return BytesNode(self.pos, value=self.bytes_value).coerce_to(dst_type, env)
if dst_type.is_pyunicode_ptr:
- node = UnicodeNode(self.pos, value=self.value)
- node.type = dst_type
- return node
+ return UnicodeNode(self.pos, value=self.value, type=dst_type)
error(self.pos,
"Unicode literals do not support coercion to C types other "
"than Py_UNICODE/Py_UCS4 (for characters) or Py_UNICODE* "
@@ -1519,20 +1717,28 @@
def generate_evaluation_code(self, code):
if self.type.is_pyobject:
- if self.contains_surrogates():
- # surrogates are not really portable and cannot be
+ # FIXME: this should go away entirely!
+ # Since string_contains_lone_surrogates() returns False for surrogate pairs in Py2/UCS2,
+ # Py2 can generate different code from Py3 here. Let's hope we get away with claiming that
+ # the processing of surrogate pairs in code was always ambiguous and lead to different results
+ # on P16/32bit Unicode platforms.
+ if StringEncoding.string_contains_lone_surrogates(self.value):
+ # lone (unpaired) surrogates are not really portable and cannot be
# decoded by the UTF-8 codec in Py3.3
self.result_code = code.get_py_const(py_object_type, 'ustring')
- data_cname = code.get_pyunicode_ptr_const(self.value)
- code = code.get_cached_constants_writer()
- code.mark_pos(self.pos)
- code.putln(
- "%s = PyUnicode_FromUnicode(%s, (sizeof(%s) / sizeof(Py_UNICODE))-1); %s" % (
+ data_cname = code.get_string_const(
+ StringEncoding.BytesLiteral(self.value.encode('unicode_escape')))
+ const_code = code.get_cached_constants_writer(self.result_code)
+ if const_code is None:
+ return # already initialised
+ const_code.mark_pos(self.pos)
+ const_code.putln(
+ "%s = PyUnicode_DecodeUnicodeEscape(%s, sizeof(%s) - 1, NULL); %s" % (
self.result_code,
data_cname,
data_cname,
- code.error_goto_if_null(self.result_code, self.pos)))
- code.put_error_if_neg(
+ const_code.error_goto_if_null(self.result_code, self.pos)))
+ const_code.put_error_if_neg(
self.pos, "__Pyx_PyUnicode_READY(%s)" % self.result_code)
else:
self.result_code = code.get_py_string_const(self.value)
@@ -1628,15 +1834,15 @@
class ImagNode(AtomicExprNode):
# Imaginary number literal
#
- # value float imaginary part
+ # value string imaginary part (float value)
type = PyrexTypes.c_double_complex_type
def calculate_constant_result(self):
- self.constant_result = complex(0.0, self.value)
+ self.constant_result = complex(0.0, float(self.value))
def compile_time_value(self, denv):
- return complex(0.0, self.value)
+ return complex(0.0, float(self.value))
def analyse_types(self, env):
self.type.create_declaration_utility_code(env)
@@ -1651,7 +1857,7 @@
node = ImagNode(self.pos, value=self.value)
if dst_type.is_pyobject:
node.is_temp = 1
- node.type = PyrexTypes.py_object_type
+ node.type = Builtin.complex_type
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
@@ -1672,7 +1878,7 @@
self.result(),
float(self.value),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class NewExprNode(AtomicExprNode):
@@ -1690,11 +1896,7 @@
self.type = error_type
return
self.cpp_check(env)
- constructor = type.scope.lookup(u'')
- if constructor is None:
- func_type = PyrexTypes.CFuncType(type, [], exception_check='+')
- type.scope.declare_cfunction(u'', func_type, self.pos)
- constructor = type.scope.lookup(u'')
+ constructor = type.get_constructor(self.pos)
self.class_type = type
self.entry = constructor
self.type = constructor.type
@@ -1729,7 +1931,7 @@
is_name = True
is_cython_module = False
cython_attribute = None
- lhs_of_first_assignment = False # TODO: remove me
+ lhs_of_first_assignment = False # TODO: remove me
is_used_as_rvalue = 0
entry = None
type_entry = None
@@ -1808,6 +2010,66 @@
return super(NameNode, self).coerce_to(dst_type, env)
+ def declare_from_annotation(self, env, as_target=False):
+ """Implements PEP 526 annotation typing in a fairly relaxed way.
+
+ Annotations are ignored for global variables.
+ All other annotations are stored on the entry in the symbol table.
+ String literals are allowed and not evaluated.
+ The ambiguous Python types 'int' and 'long' are not evaluated - the 'cython.int' form must be used instead.
+ """
+ name = self.name
+ annotation = self.annotation
+ entry = self.entry or env.lookup_here(name)
+ if not entry:
+ # annotations never create global cdef names
+ if env.is_module_scope:
+ return
+ if (
+ # name: "description" => not a type, but still a declared variable or attribute
+ annotation.expr.is_string_literal
+ # don't do type analysis from annotations if not asked to, but still collect the annotation
+ or not env.directives['annotation_typing']
+ ):
+ atype = None
+ elif env.is_py_class_scope:
+ # For Python class scopes every attribute is a Python object
+ atype = py_object_type
+ else:
+ _, atype = annotation.analyse_type_annotation(env)
+ if atype is None:
+ atype = unspecified_type if as_target and env.directives['infer_types'] != False else py_object_type
+ if atype.is_fused and env.fused_to_specific:
+ try:
+ atype = atype.specialize(env.fused_to_specific)
+ except CannotSpecialize:
+ error(self.pos,
+ "'%s' cannot be specialized since its type is not a fused argument to this function" %
+ self.name)
+ atype = error_type
+ visibility = 'private'
+ if 'dataclasses.dataclass' in env.directives:
+ # handle "frozen" directive - full inspection of the dataclass directives happens
+ # in Dataclass.py
+ frozen_directive = None
+ dataclass_directive = env.directives['dataclasses.dataclass']
+ if dataclass_directive:
+ dataclass_directive_kwds = dataclass_directive[1]
+ frozen_directive = dataclass_directive_kwds.get('frozen', None)
+ is_frozen = frozen_directive and frozen_directive.is_literal and frozen_directive.value
+ if atype.is_pyobject or atype.can_coerce_to_pyobject(env):
+ visibility = 'readonly' if is_frozen else 'public'
+ # If the object can't be coerced that's fine - we just don't create a property
+ if as_target and env.is_c_class_scope and not (atype.is_pyobject or atype.is_error):
+ # TODO: this will need revising slightly if annotated cdef attributes are implemented
+ atype = py_object_type
+ warning(annotation.pos, "Annotation ignored since class-level attributes must be Python objects. "
+ "Were you trying to set up an instance attribute?", 2)
+ entry = self.entry = env.declare_var(name, atype, self.pos, is_cdef=not as_target, visibility=visibility)
+ # Even if the entry already exists, make sure we're supplying an annotation if we can.
+ if annotation and not entry.annotation:
+ entry.annotation = annotation
+
def analyse_as_module(self, env):
# Try to interpret this as a reference to a cimported module.
# Returns the module scope, or None.
@@ -1816,6 +2078,10 @@
entry = env.lookup(self.name)
if entry and entry.as_module:
return entry.as_module
+ if entry and entry.known_standard_library_import:
+ scope = Builtin.get_known_standard_library_module_scope(entry.known_standard_library_import)
+ if scope and scope.is_module_scope:
+ return scope
return None
def analyse_as_type(self, env):
@@ -1830,6 +2096,10 @@
entry = env.lookup(self.name)
if entry and entry.is_type:
return entry.type
+ elif entry and entry.known_standard_library_import:
+ entry = Builtin.get_known_standard_library_entry(entry.known_standard_library_import)
+ if entry and entry.is_type:
+ return entry.type
else:
return None
@@ -1845,8 +2115,26 @@
return None
def analyse_target_declaration(self, env):
+ return self._analyse_target_declaration(env, is_assignment_expression=False)
+
+ def analyse_assignment_expression_target_declaration(self, env):
+ return self._analyse_target_declaration(env, is_assignment_expression=True)
+
+ def _analyse_target_declaration(self, env, is_assignment_expression):
+ self.is_target = True
if not self.entry:
- self.entry = env.lookup_here(self.name)
+ if is_assignment_expression:
+ self.entry = env.lookup_assignment_expression_target(self.name)
+ else:
+ self.entry = env.lookup_here(self.name)
+ if self.entry:
+ self.entry.known_standard_library_import = "" # already exists somewhere and so is now ambiguous
+ if not self.entry and self.annotation is not None:
+ # name : type = ...
+ is_dataclass = 'dataclasses.dataclass' in env.directives
+ # In a dataclass, an assignment should not prevent a name from becoming an instance attribute.
+ # Hence, "as_target = not is_dataclass".
+ self.declare_from_annotation(env, as_target=not is_dataclass)
if not self.entry:
if env.directives['warn.undeclared']:
warning(self.pos, "implicit declaration of '%s'" % self.name, 1)
@@ -1854,25 +2142,33 @@
type = unspecified_type
else:
type = py_object_type
- self.entry = env.declare_var(self.name, type, self.pos)
+ if is_assignment_expression:
+ self.entry = env.declare_assignment_expression_target(self.name, type, self.pos)
+ else:
+ self.entry = env.declare_var(self.name, type, self.pos)
if self.entry.is_declared_generic:
self.result_ctype = py_object_type
+ if self.entry.as_module:
+ # cimported modules namespace can shadow actual variables
+ self.entry.is_variable = 1
def analyse_types(self, env):
self.initialized_check = env.directives['initializedcheck']
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if not self.entry:
- self.entry = env.declare_builtin(self.name, self.pos)
- if not self.entry:
- self.type = PyrexTypes.error_type
- return self
entry = self.entry
- if entry:
- entry.used = 1
- if entry.type.is_buffer:
- from . import Buffer
- Buffer.used_buffer_aux_vars(entry)
+ if entry is None:
+ entry = env.lookup(self.name)
+ if not entry:
+ entry = env.declare_builtin(self.name, self.pos)
+ if entry and entry.is_builtin and entry.is_const:
+ self.is_literal = True
+ if not entry:
+ self.type = PyrexTypes.error_type
+ return self
+ self.entry = entry
+ entry.used = 1
+ if entry.type.is_buffer:
+ from . import Buffer
+ Buffer.used_buffer_aux_vars(entry)
self.analyse_rvalue_entry(env)
return self
@@ -1889,8 +2185,6 @@
if self.type.is_const:
error(self.pos, "Assignment to const '%s'" % self.name)
- if self.type.is_reference:
- error(self.pos, "Assignment to reference '%s'" % self.name)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
self.type = PyrexTypes.error_type
@@ -1927,7 +2221,7 @@
if self.is_used_as_rvalue:
entry = self.entry
if entry.is_builtin:
- if not entry.is_const: # cached builtins are ok
+ if not entry.is_const: # cached builtins are ok
self.gil_error()
elif entry.is_pyglobal:
self.gil_error()
@@ -1952,19 +2246,24 @@
entry = self.entry
if entry.is_type and entry.type.is_extension_type:
self.type_entry = entry
- if entry.is_type and entry.type.is_enum:
+ if entry.is_type and (entry.type.is_enum or entry.type.is_cpp_enum):
py_entry = Symtab.Entry(self.name, None, py_object_type)
py_entry.is_pyglobal = True
py_entry.scope = self.entry.scope
self.entry = py_entry
- elif not (entry.is_const or entry.is_variable
- or entry.is_builtin or entry.is_cfunction
- or entry.is_cpp_class):
- if self.entry.as_variable:
- self.entry = self.entry.as_variable
- else:
- error(self.pos,
- "'%s' is not a constant, variable or function identifier" % self.name)
+ elif not (entry.is_const or entry.is_variable or
+ entry.is_builtin or entry.is_cfunction or
+ entry.is_cpp_class):
+ if self.entry.as_variable:
+ self.entry = self.entry.as_variable
+ elif not self.is_cython_module:
+ error(self.pos, "'%s' is not a constant, variable or function identifier" % self.name)
+
+ def is_cimported_module_without_shadow(self, env):
+ if self.is_cython_module or self.cython_attribute:
+ return False
+ entry = self.entry or env.lookup(self.name)
+ return entry.as_module and not entry.is_variable
def is_simple(self):
# If it's not a C variable, it'll be in a temp.
@@ -1973,7 +2272,7 @@
def may_be_none(self):
if self.cf_state and self.type and (self.type.is_pyobject or
self.type.is_memoryviewslice):
- # gard against infinite recursion on self-dependencies
+ # guard against infinite recursion on self-dependencies
if getattr(self, '_none_checking', False):
# self-dependency - either this node receives a None
# value from *another* node, or it can not reference
@@ -2004,7 +2303,11 @@
def check_const(self):
entry = self.entry
- if entry is not None and not (entry.is_const or entry.is_cfunction or entry.is_builtin):
+ if entry is not None and not (
+ entry.is_const or
+ entry.is_cfunction or
+ entry.is_builtin or
+ entry.type.is_const):
self.not_const()
return False
return True
@@ -2036,22 +2339,25 @@
def calculate_result_code(self):
entry = self.entry
if not entry:
- return "" # There was an error earlier
+ return "" # There was an error earlier
+ if self.entry.is_cpp_optional and not self.is_target:
+ return "(*%s)" % entry.cname
return entry.cname
def generate_result_code(self, code):
- assert hasattr(self, 'entry')
entry = self.entry
if entry is None:
- return # There was an error earlier
+ return # There was an error earlier
+ if entry.utility_code:
+ code.globalstate.use_utility_code(entry.utility_code)
if entry.is_builtin and entry.is_const:
- return # Lookup already cached
+ return # Lookup already cached
elif entry.is_pyclass_attr:
assert entry.type.is_pyobject, "Python global or builtin not a Python object"
interned_cname = code.intern_identifier(self.entry.name)
if entry.is_builtin:
namespace = Naming.builtins_cname
- else: # entry.is_pyglobal
+ else: # entry.is_pyglobal
namespace = entry.scope.namespace_cname
if not self.cf_is_null:
code.putln(
@@ -2064,13 +2370,13 @@
code.globalstate.use_utility_code(
UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
code.putln(
- '%s = __Pyx_GetModuleGlobalName(%s);' % (
+ '__Pyx_GetModuleGlobalName(%s, %s);' % (
self.result(),
interned_cname))
if not self.cf_is_null:
code.putln("}")
code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
elif entry.is_builtin and not entry.scope.is_module_scope:
# known builtin
@@ -2083,7 +2389,7 @@
self.result(),
interned_cname,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope):
# name in class body, global name or unknown builtin
@@ -2093,7 +2399,7 @@
code.globalstate.use_utility_code(
UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
code.putln(
- '%s = __Pyx_GetModuleGlobalName(%s); %s' % (
+ '__Pyx_GetModuleGlobalName(%s, %s); %s' % (
self.result(),
interned_cname,
code.error_goto_if_null(self.result(), self.pos)))
@@ -2102,30 +2408,39 @@
code.globalstate.use_utility_code(
UtilityCode.load_cached("GetNameInClass", "ObjectHandling.c"))
code.putln(
- '%s = __Pyx_GetNameInClass(%s, %s); %s' % (
+ '__Pyx_GetNameInClass(%s, %s, %s); %s' % (
self.result(),
entry.scope.namespace_cname,
interned_cname,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
# Raise UnboundLocalError for objects and memoryviewslices
raise_unbound = (
(self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
- null_code = entry.type.check_for_null_code(entry.cname)
memslice_check = entry.type.is_memoryviewslice and self.initialized_check
+ optional_cpp_check = entry.is_cpp_optional and self.initialized_check
+
+ if optional_cpp_check:
+ unbound_check_code = entry.type.cpp_optional_check_for_null_code(entry.cname)
+ else:
+ unbound_check_code = entry.type.check_for_null_code(entry.cname)
- if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
- code.put_error_if_unbound(self.pos, entry, self.in_nogil_context)
+ if unbound_check_code and raise_unbound and (entry.type.is_pyobject or memslice_check or optional_cpp_check):
+ code.put_error_if_unbound(self.pos, entry, self.in_nogil_context, unbound_check_code=unbound_check_code)
+
+ elif entry.is_cglobal and entry.is_cpp_optional and self.initialized_check:
+ unbound_check_code = entry.type.cpp_optional_check_for_null_code(entry.cname)
+ code.put_error_if_unbound(self.pos, entry, unbound_check_code=unbound_check_code)
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
#print "NameNode.generate_assignment_code:", self.name ###
entry = self.entry
if entry is None:
- return # There was an error earlier
+ return # There was an error earlier
if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
and not self.lhs_of_first_assignment and not rhs.in_module_scope):
@@ -2146,7 +2461,10 @@
setter = 'PyDict_SetItem'
namespace = Naming.moddict_cname
elif entry.is_pyclass_attr:
- setter = 'PyObject_SetItem'
+ # Special-case setting __new__
+ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass"
+ code.globalstate.use_utility_code(UtilityCode.load_cached(n, "ObjectHandling.c"))
+ setter = '__Pyx_' + n
else:
assert False, repr(entry)
code.put_error_if_neg(
@@ -2188,38 +2506,39 @@
rhs.make_owned_reference(code)
is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
if is_external_ref:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xgotref(self.py_result())
- else:
- code.put_gotref(self.py_result())
+ self.generate_gotref(code, handle_null=True)
assigned = True
if entry.is_cglobal:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
+ self.generate_decref_set(code, rhs.result_as(self.ctype()))
else:
if not self.cf_is_null:
if self.cf_maybe_null:
- code.put_xdecref_set(
- self.result(), rhs.result_as(self.ctype()))
+ self.generate_xdecref_set(code, rhs.result_as(self.ctype()))
else:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
+ self.generate_decref_set(code, rhs.result_as(self.ctype()))
else:
assigned = False
if is_external_ref:
- code.put_giveref(rhs.py_result())
+ rhs.generate_giveref(code)
if not self.type.is_memoryviewslice:
if not assigned:
if overloaded_assignment:
- result = rhs.result()
+ result = rhs.move_result_rhs()
if exception_check == '+':
- translate_cpp_exception(code, self.pos, '%s = %s;' % (self.result(), result), exception_value, self.in_nogil_context)
+ translate_cpp_exception(
+ code, self.pos,
+ '%s = %s;' % (self.result(), result),
+ self.result() if self.type.is_pyobject else None,
+ exception_value, self.in_nogil_context)
else:
code.putln('%s = %s;' % (self.result(), result))
else:
- result = rhs.result_as(self.ctype())
- code.putln('%s = %s;' % (self.result(), result))
+ result = rhs.move_result_rhs_as(self.ctype())
+
+ if is_pythran_expr(self.type):
+ code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result))
+ elif result != self.result():
+ code.putln('%s = %s;' % (self.result(), result))
if debug_disposal_code:
print("NameNode.generate_assignment_code:")
print("...generating post-assignment code for %s" % rhs)
@@ -2267,7 +2586,7 @@
def generate_deletion_code(self, code, ignore_nonexisting=False):
if self.entry is None:
- return # There was an error earlier
+ return # There was an error earlier
elif self.entry.is_pyclass_attr:
namespace = self.entry.scope.namespace_cname
interned_cname = code.intern_identifier(self.entry.name)
@@ -2303,26 +2622,20 @@
if self.cf_maybe_null and not ignore_nonexisting:
code.put_error_if_unbound(self.pos, self.entry)
- if self.entry.type.is_pyobject:
- if self.entry.in_closure:
- # generator
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xgotref(self.result())
- else:
- code.put_gotref(self.result())
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xdecref(self.result(), self.ctype())
- else:
- code.put_decref(self.result(), self.ctype())
- code.putln('%s = NULL;' % self.result())
+ if self.entry.in_closure:
+ # generator
+ self.generate_gotref(code, handle_null=True, maybe_null_extra_check=ignore_nonexisting)
+ if ignore_nonexisting and self.cf_maybe_null:
+ code.put_xdecref_clear(self.result(), self.ctype(),
+ have_gil=not self.nogil)
else:
- code.put_xdecref_memoryviewslice(self.entry.cname,
- have_gil=not self.nogil)
+ code.put_decref_clear(self.result(), self.ctype(),
+ have_gil=not self.nogil)
else:
error(self.pos, "Deletion of C names not supported")
def annotate(self, code):
- if hasattr(self, 'is_called') and self.is_called:
+ if getattr(self, 'is_called', False):
pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
if self.type.is_pyobject:
style, text = 'py_call', 'python function (%s)'
@@ -2330,6 +2643,11 @@
style, text = 'c_call', 'c function (%s)'
code.annotate(pos, AnnotationItem(style, text % self.type, size=len(self.name)))
+ def get_known_standard_library_import(self):
+ if self.entry:
+ return self.entry.known_standard_library_import
+ return None
+
class BackquoteNode(ExprNode):
# `expr`
#
@@ -2356,7 +2674,7 @@
self.result(),
self.arg.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class ImportNode(ExprNode):
@@ -2375,44 +2693,61 @@
# relative to the current module.
# None: decide the level according to language level and
# directives
+ # get_top_level_module int true: return top-level module, false: return imported module
+ # module_names TupleNode the separate names of the module and submodules, or None
type = py_object_type
+ module_names = None
+ get_top_level_module = False
+ is_temp = True
- subexprs = ['module_name', 'name_list']
+ subexprs = ['module_name', 'name_list', 'module_names']
def analyse_types(self, env):
if self.level is None:
- if (env.directives['py2_import'] or
- Future.absolute_import not in env.global_scope().context.future_directives):
+ # For modules in packages, and without 'absolute_import' enabled, try relative (Py2) import first.
+ if env.global_scope().parent_module and (
+ env.directives['py2_import'] or
+ Future.absolute_import not in env.global_scope().context.future_directives):
self.level = -1
else:
self.level = 0
module_name = self.module_name.analyse_types(env)
self.module_name = module_name.coerce_to_pyobject(env)
+ assert self.module_name.is_string_literal
if self.name_list:
name_list = self.name_list.analyse_types(env)
self.name_list = name_list.coerce_to_pyobject(env)
- self.is_temp = 1
+ elif '.' in self.module_name.value:
+ self.module_names = TupleNode(self.module_name.pos, args=[
+ IdentifierStringNode(self.module_name.pos, value=part, constant_result=part)
+ for part in map(StringEncoding.EncodedString, self.module_name.value.split('.'))
+ ]).analyse_types(env)
return self
gil_message = "Python import"
def generate_result_code(self, code):
- if self.name_list:
- name_list_code = self.name_list.py_result()
+ assert self.module_name.is_string_literal
+ module_name = self.module_name.value
+
+ if self.level == 0 and not self.name_list and not self.get_top_level_module:
+ if self.module_names:
+ assert self.module_names.is_literal # make sure we create the tuple only once
+ code.globalstate.use_utility_code(UtilityCode.load_cached("ImportDottedModule", "ImportExport.c"))
+ import_code = "__Pyx_ImportDottedModule(%s, %s)" % (
+ self.module_name.py_result(),
+ self.module_names.py_result() if self.module_names else 'NULL',
+ )
else:
- name_list_code = "0"
+ code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c"))
+ import_code = "__Pyx_Import(%s, %s, %d)" % (
+ self.module_name.py_result(),
+ self.name_list.py_result() if self.name_list else '0',
+ self.level)
- code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c"))
- import_code = "__Pyx_Import(%s, %s, %d)" % (
- self.module_name.py_result(),
- name_list_code,
- self.level)
-
- if (self.level <= 0 and
- self.module_name.is_string_literal and
- self.module_name.value in utility_code_for_imports):
- helper_func, code_name, code_file = utility_code_for_imports[self.module_name.value]
+ if self.level <= 0 and module_name in utility_code_for_imports:
+ helper_func, code_name, code_file = utility_code_for_imports[module_name]
code.globalstate.use_utility_code(UtilityCode.load_cached(code_name, code_file))
import_code = '%s(%s)' % (helper_func, import_code)
@@ -2420,7 +2755,10 @@
self.result(),
import_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
+
+ def get_known_standard_library_import(self):
+ return self.module_name.value
class IteratorNode(ExprNode):
@@ -2433,7 +2771,6 @@
type = py_object_type
iter_func_ptr = None
counter_cname = None
- cpp_iterator_cname = None
reversed = False # currently only used for list/tuple types (see Optimize.py)
is_async = False
@@ -2446,7 +2783,7 @@
# C array iteration will be transformed later on
self.type = self.sequence.type
elif self.sequence.type.is_cpp_class:
- self.analyse_cpp_types(env)
+ return CppIteratorNode(self.pos, sequence=self.sequence).analyse_types(env)
else:
self.sequence = self.sequence.coerce_to_pyobject(env)
if self.sequence.type in (list_type, tuple_type):
@@ -2476,65 +2813,10 @@
return sequence_type
return py_object_type
- def analyse_cpp_types(self, env):
- sequence_type = self.sequence.type
- if sequence_type.is_ptr:
- sequence_type = sequence_type.base_type
- begin = sequence_type.scope.lookup("begin")
- end = sequence_type.scope.lookup("end")
- if (begin is None
- or not begin.type.is_cfunction
- or begin.type.args):
- error(self.pos, "missing begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if (end is None
- or not end.type.is_cfunction
- or end.type.args):
- error(self.pos, "missing end() on %s" % self.sequence.type)
- self.type = error_type
- return
- iter_type = begin.type.return_type
- if iter_type.is_cpp_class:
- if env.lookup_operator_for_types(
- self.pos,
- "!=",
- [iter_type, end.type.return_type]) is None:
- error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
- error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
- error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- self.type = iter_type
- elif iter_type.is_ptr:
- if not (iter_type == end.type.return_type):
- error(self.pos, "incompatible types for begin() and end()")
- self.type = iter_type
- else:
- error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
- self.type = error_type
- return
-
def generate_result_code(self, code):
sequence_type = self.sequence.type
if sequence_type.is_cpp_class:
- if self.sequence.is_name:
- # safe: C++ won't allow you to reassign to class references
- begin_func = "%s.begin" % self.sequence.result()
- else:
- sequence_type = PyrexTypes.c_ptr_type(sequence_type)
- self.cpp_iterator_cname = code.funcstate.allocate_temp(sequence_type, manage_ref=False)
- code.putln("%s = &%s;" % (self.cpp_iterator_cname, self.sequence.result()))
- begin_func = "%s->begin" % self.cpp_iterator_cname
- # TODO: Limit scope.
- code.putln("%s = %s();" % (self.result(), begin_func))
- return
+ assert False, "Should have been changed to CppIteratorNode"
if sequence_type.is_array or sequence_type.is_ptr:
raise InternalError("for in carray slice not transformed")
@@ -2576,12 +2858,12 @@
self.result(),
self.sequence.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
# PyObject_GetIter() fails if "tp_iternext" is not set, but the check below
# makes it visible to the C compiler that the pointer really isn't NULL, so that
# it can distinguish between the special cases and the generic case
- code.putln("%s = Py_TYPE(%s)->tp_iternext; %s" % (
+ code.putln("%s = __Pyx_PyObject_GetIterNextFunc(%s); %s" % (
self.iter_func_ptr, self.py_result(),
code.error_goto_if_null(self.iter_func_ptr, self.pos)))
if self.may_be_a_sequence:
@@ -2623,28 +2905,14 @@
self.counter_cname,
inc_dec,
code.error_goto_if_null(result_name, self.pos)))
- code.put_gotref(result_name)
+ code.put_gotref(result_name, py_object_type)
code.putln("#endif")
def generate_iter_next_result_code(self, result_name, code):
sequence_type = self.sequence.type
if self.reversed:
code.putln("if (%s < 0) break;" % self.counter_cname)
- if sequence_type.is_cpp_class:
- if self.cpp_iterator_cname:
- end_func = "%s->end" % self.cpp_iterator_cname
- else:
- end_func = "%s.end" % self.sequence.result()
- # TODO: Cache end() call?
- code.putln("if (!(%s != %s())) break;" % (
- self.result(),
- end_func))
- code.putln("%s = *%s;" % (
- result_name,
- self.result()))
- code.putln("++%s;" % self.result())
- return
- elif sequence_type is list_type:
+ if sequence_type is list_type:
self.generate_next_sequence_item('List', result_name, code)
return
elif sequence_type is tuple_type:
@@ -2669,13 +2937,12 @@
code.putln("if (unlikely(!%s)) {" % result_name)
code.putln("PyObject* exc_type = PyErr_Occurred();")
code.putln("if (exc_type) {")
- code.putln("if (likely(exc_type == PyExc_StopIteration ||"
- " PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
+ code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
code.putln("else %s" % code.error_goto(self.pos))
code.putln("}")
code.putln("break;")
code.putln("}")
- code.put_gotref(result_name)
+ code.put_gotref(result_name, py_object_type)
code.putln("}")
def free_temps(self, code):
@@ -2684,8 +2951,114 @@
if self.iter_func_ptr:
code.funcstate.release_temp(self.iter_func_ptr)
self.iter_func_ptr = None
- if self.cpp_iterator_cname:
- code.funcstate.release_temp(self.cpp_iterator_cname)
+ ExprNode.free_temps(self, code)
+
+
+class CppIteratorNode(ExprNode):
+ # Iteration over a C++ container.
+ # Created at the analyse_types stage by IteratorNode
+ cpp_sequence_cname = None
+ cpp_attribute_op = "."
+ extra_dereference = ""
+ is_temp = True
+
+ subexprs = ['sequence']
+
+ def analyse_types(self, env):
+ sequence_type = self.sequence.type
+ if sequence_type.is_ptr:
+ sequence_type = sequence_type.base_type
+ begin = sequence_type.scope.lookup("begin")
+ end = sequence_type.scope.lookup("end")
+ if (begin is None
+ or not begin.type.is_cfunction
+ or begin.type.args):
+ error(self.pos, "missing begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return self
+ if (end is None
+ or not end.type.is_cfunction
+ or end.type.args):
+ error(self.pos, "missing end() on %s" % self.sequence.type)
+ self.type = error_type
+ return self
+ iter_type = begin.type.return_type
+ if iter_type.is_cpp_class:
+ if env.directives['cpp_locals']:
+ self.extra_dereference = "*"
+ if env.lookup_operator_for_types(
+ self.pos,
+ "!=",
+ [iter_type, end.type.return_type]) is None:
+ error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return self
+ if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
+ error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return self
+ if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
+ error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return self
+ self.type = iter_type
+ elif iter_type.is_ptr:
+ if not (iter_type == end.type.return_type):
+ error(self.pos, "incompatible types for begin() and end()")
+ self.type = iter_type
+ else:
+ error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
+ self.type = error_type
+ return self
+
+ def generate_result_code(self, code):
+ sequence_type = self.sequence.type
+ # essentially 3 options:
+ if self.sequence.is_name or self.sequence.is_attribute:
+ # 1) is a name and can be accessed directly;
+ # assigning to it may break the container, but that's the responsibility
+ # of the user
+ code.putln("%s = %s%sbegin();" % (self.result(),
+ self.sequence.result(),
+ self.cpp_attribute_op))
+ else:
+ # (while it'd be nice to limit the scope of the loop temp, it's essentially
+ # impossible to do while supporting generators)
+ temp_type = sequence_type
+ if temp_type.is_reference:
+ # 2) Sequence is a reference (often obtained by dereferencing a pointer);
+ # make the temp a pointer so we are not sensitive to users reassigning
+ # the pointer than it came from
+ temp_type = PyrexTypes.CPtrType(sequence_type.ref_base_type)
+ if temp_type.is_ptr or code.globalstate.directives['cpp_locals']:
+ self.cpp_attribute_op = "->"
+ # 3) (otherwise) sequence comes from a function call or similar, so we must
+ # create a temp to store it in
+ self.cpp_sequence_cname = code.funcstate.allocate_temp(temp_type, manage_ref=False)
+ code.putln("%s = %s%s;" % (self.cpp_sequence_cname,
+ "&" if temp_type.is_ptr else "",
+ self.sequence.move_result_rhs()))
+ code.putln("%s = %s%sbegin();" % (self.result(), self.cpp_sequence_cname,
+ self.cpp_attribute_op))
+
+ def generate_iter_next_result_code(self, result_name, code):
+ # end call isn't cached to support containers that allow adding while iterating
+ # (much as this is usually a bad idea)
+ code.putln("if (!(%s%s != %s%send())) break;" % (
+ self.extra_dereference,
+ self.result(),
+ self.cpp_sequence_cname or self.sequence.result(),
+ self.cpp_attribute_op))
+ code.putln("%s = *%s%s;" % (
+ result_name,
+ self.extra_dereference,
+ self.result()))
+ code.putln("++%s%s;" % (self.extra_dereference, self.result()))
+
+ def free_temps(self, code):
+ if self.cpp_sequence_cname:
+ code.funcstate.release_temp(self.cpp_sequence_cname)
+ # skip over IteratorNode since we don't use any of the temps it does
ExprNode.free_temps(self, code)
@@ -2717,8 +3090,8 @@
item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
if item_type.is_reference:
item_type = item_type.ref_base_type
- if item_type.is_const:
- item_type = item_type.const_base_type
+ if item_type.is_cv_qualified:
+ item_type = item_type.cv_base_type
return item_type
else:
# Avoid duplication of complicated logic.
@@ -2767,7 +3140,7 @@
self.result(),
self.sequence.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
class AsyncNextNode(AtomicExprNode):
@@ -2797,25 +3170,25 @@
self.result(),
self.iterator.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
class WithExitCallNode(ExprNode):
# The __exit__() call of a 'with' statement. Used in both the
# except and finally clauses.
- # with_stat WithStatNode the surrounding 'with' statement
- # args TupleNode or ResultStatNode the exception info tuple
- # await AwaitExprNode the await expression of an 'async with' statement
+ # with_stat WithStatNode the surrounding 'with' statement
+ # args TupleNode or ResultStatNode the exception info tuple
+ # await_expr AwaitExprNode the await expression of an 'async with' statement
- subexprs = ['args', 'await']
+ subexprs = ['args', 'await_expr']
test_if_run = True
- await = None
+ await_expr = None
def analyse_types(self, env):
self.args = self.args.analyse_types(env)
- if self.await:
- self.await = self.await.analyse_types(env)
+ if self.await_expr:
+ self.await_expr = self.await_expr.analyse_types(env)
self.type = PyrexTypes.c_bint_type
self.is_temp = True
return self
@@ -2840,14 +3213,14 @@
self.args.free_temps(code)
code.putln(code.error_goto_if_null(result_var, self.pos))
- code.put_gotref(result_var)
+ code.put_gotref(result_var, py_object_type)
- if self.await:
+ if self.await_expr:
# FIXME: result_var temp currently leaks into the closure
- self.await.generate_evaluation_code(code, source_cname=result_var, decref_source=True)
- code.putln("%s = %s;" % (result_var, self.await.py_result()))
- self.await.generate_post_assignment_code(code)
- self.await.free_temps(code)
+ self.await_expr.generate_evaluation_code(code, source_cname=result_var, decref_source=True)
+ code.putln("%s = %s;" % (result_var, self.await_expr.py_result()))
+ self.await_expr.generate_post_assignment_code(code)
+ self.await_expr.free_temps(code)
if self.result_is_used:
self.allocate_temp_result(code)
@@ -2905,7 +3278,7 @@
return self
def analyse_target_declaration(self, env):
- pass
+ self.is_target = True
def generate_result_code(self, code):
pass
@@ -2972,6 +3345,7 @@
#
type = unicode_type
is_temp = True
+ gil_message = "String concatenation"
subexprs = ['values']
@@ -2994,7 +3368,7 @@
list_var,
num_items,
code.error_goto_if_null(list_var, self.pos)))
- code.put_gotref(list_var)
+ code.put_gotref(list_var, py_object_type)
code.putln("%s = 0;" % ulength_var)
code.putln("%s = 127;" % max_char_var) # at least ASCII character range
@@ -3007,12 +3381,27 @@
is_ascii = False
if isinstance(node, UnicodeNode):
try:
+ # most strings will be ASCII or at least Latin-1
node.value.encode('iso8859-1')
max_char_value = '255'
node.value.encode('us-ascii')
is_ascii = True
except UnicodeEncodeError:
- pass
+ if max_char_value != '255':
+ # not ISO8859-1 => check BMP limit
+ max_char = max(map(ord, node.value))
+ if max_char < 0xD800:
+ # BMP-only, no surrogate pairs used
+ max_char_value = '65535'
+ ulength = str(len(node.value))
+ elif max_char >= 65536:
+ # cleary outside of BMP, and not on a 16-bit Unicode system
+ max_char_value = '1114111'
+ ulength = str(len(node.value))
+ else:
+ # not really worth implementing a check for surrogate pairs here
+ # drawback: C code can differ when generating on Py2 with 2-byte Unicode
+ pass
else:
ulength = str(len(node.value))
elif isinstance(node, FormattedValueNode) and node.value.type.is_numeric:
@@ -3023,7 +3412,7 @@
max_char_var, max_char_value, max_char_var, max_char_value, max_char_var))
code.putln("%s += %s;" % (ulength_var, ulength))
- code.put_giveref(node.py_result())
+ node.generate_giveref(code)
code.putln('PyTuple_SET_ITEM(%s, %s, %s);' % (list_var, i, node.py_result()))
node.generate_post_assignment_code(code)
node.free_temps(code)
@@ -3038,7 +3427,7 @@
ulength_var,
max_char_var,
code.error_goto_if_null(self.py_result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
code.put_decref_clear(list_var, py_object_type)
code.funcstate.release_temp(list_var)
@@ -3050,7 +3439,7 @@
# {}-delimited portions of an f-string
#
# value ExprNode The expression itself
- # conversion_char str or None Type conversion (!s, !r, !a, or none)
+ # conversion_char str or None Type conversion (!s, !r, !a, none, or 'd' for integer conversion)
# format_spec JoinedStrNode or None Format string passed to __format__
# c_format_spec str or None If not None, formatting can be done at the C level
@@ -3059,11 +3448,13 @@
type = unicode_type
is_temp = True
c_format_spec = None
+ gil_message = "String formatting"
find_conversion_func = {
- 's': 'PyObject_Str',
+ 's': 'PyObject_Unicode',
'r': 'PyObject_Repr',
'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2
+ 'd': '__Pyx_PyNumber_IntOrLong', # NOTE: internal mapping for '%d' formatting
}.get
def may_be_none(self):
@@ -3081,7 +3472,7 @@
self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
if self.c_format_spec is None:
self.value = self.value.coerce_to_pyobject(env)
- if not self.format_spec and not self.conversion_char:
+ if not self.format_spec and (not self.conversion_char or self.conversion_char == 's'):
if self.value.type is unicode_type and not self.value.may_be_none():
# value is definitely a unicode string and we don't format it any special
return self.value
@@ -3095,7 +3486,7 @@
self.result(),
convert_func_call,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
return
value_result = self.value.py_result()
@@ -3134,7 +3525,7 @@
value_result,
format_spec,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
#-------------------------------------------------------------------
@@ -3172,7 +3563,7 @@
return self.temp_code
-class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
+class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
"""
Implements cython.parallel.threadid()
"""
@@ -3211,7 +3602,7 @@
# in most cases, indexing will return a safe reference to an object in a container,
# so we consider the result safe if the base object is
return self.base.is_ephemeral() or self.base.type in (
- basestring_type, str_type, bytes_type, unicode_type)
+ basestring_type, str_type, bytes_type, bytearray_type, unicode_type)
def check_const_addr(self):
return self.base.check_const_addr() and self.index.check_const()
@@ -3248,10 +3639,6 @@
is_subscript = True
is_fused_index = False
- def __init__(self, pos, index, **kw):
- ExprNode.__init__(self, pos, index=index, **kw)
- self._index = index
-
def calculate_constant_result(self):
self.constant_result = self.base.constant_result[self.index.constant_result]
@@ -3275,7 +3662,7 @@
return False
if isinstance(self.index, SliceNode):
# slicing!
- if base_type in (bytes_type, str_type, unicode_type,
+ if base_type in (bytes_type, bytearray_type, str_type, unicode_type,
basestring_type, list_type, tuple_type):
return False
return ExprNode.may_be_none(self)
@@ -3285,9 +3672,9 @@
def analyse_as_type(self, env):
base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if base_type.is_cpp_class:
- if isinstance(self.index, TupleNode):
+ if base_type and (not base_type.is_pyobject or base_type.python_type_constructor_name):
+ if base_type.is_cpp_class or base_type.python_type_constructor_name:
+ if self.index.is_sequence_constructor:
template_values = self.index.args
else:
template_values = [self.index]
@@ -3296,10 +3683,22 @@
positional_args=template_values,
keyword_args=None)
return type_node.analyse(env, base_type=base_type)
+ elif self.index.is_slice or self.index.is_sequence_constructor:
+ # memory view
+ from . import MemoryView
+ env.use_utility_code(MemoryView.view_utility_code)
+ axes = [self.index] if self.index.is_slice else list(self.index.args)
+ return PyrexTypes.MemoryViewSliceType(base_type, MemoryView.get_axes_specs(env, axes))
else:
+ # C array
index = self.index.compile_time_value(env)
if index is not None:
- return PyrexTypes.CArrayType(base_type, int(index))
+ try:
+ index = int(index)
+ except (ValueError, TypeError):
+ pass
+ else:
+ return PyrexTypes.CArrayType(base_type, index)
error(self.pos, "Array size must be a compile time constant")
return None
@@ -3320,6 +3719,8 @@
bytearray_type, list_type, tuple_type):
# slicing these returns the same type
return base_type
+ elif base_type.is_memoryviewslice:
+ return base_type
else:
# TODO: Handle buffers (hopefully without too much redundancy).
return py_object_type
@@ -3362,6 +3763,23 @@
index += base_type.size
if 0 <= index < base_type.size:
return base_type.components[index]
+ elif base_type.is_memoryviewslice:
+ if base_type.ndim == 0:
+ pass # probably an error, but definitely don't know what to do - return pyobject for now
+ if base_type.ndim == 1:
+ return base_type.dtype
+ else:
+ return PyrexTypes.MemoryViewSliceType(base_type.dtype, base_type.axes[1:])
+
+ if self.index.is_sequence_constructor and base_type.is_memoryviewslice:
+ inferred_type = base_type
+ for a in self.index.args:
+ if not inferred_type.is_memoryviewslice:
+ break # something's gone wrong
+ inferred_type = IndexNode(self.pos, base=ExprNode(self.base.pos, type=inferred_type),
+ index=a).infer_type(env)
+ else:
+ return inferred_type
if base_type.is_cpp_class:
class FakeOperand:
@@ -3375,6 +3793,10 @@
if index_func is not None:
return index_func.type.return_type
+ if is_pythran_expr(base_type) and is_pythran_expr(index_type):
+ index_with_type = (self.index, index_type)
+ return PythranExpr(pythran_indexing_type(base_type, [index_with_type]))
+
# may be slicing or indexing, we don't know
if base_type in (unicode_type, str_type):
# these types always returns their own type on Python indexing/slicing
@@ -3435,6 +3857,8 @@
if not base_type.is_cfunction:
self.index = self.index.analyse_types(env)
self.original_index_type = self.index.type
+ if self.original_index_type.is_reference:
+ self.original_index_type = self.original_index_type.ref_base_type
if base_type.is_unicode_char:
# we infer Py_UNICODE/Py_UCS4 for unicode strings in some
@@ -3466,14 +3890,22 @@
def analyse_as_pyobject(self, env, is_slice, getting, setting):
base_type = self.base.type
- if self.index.type.is_int and base_type is not dict_type:
+ if self.index.type.is_unicode_char and base_type is not dict_type:
+ # TODO: eventually fold into case below and remove warning, once people have adapted their code
+ warning(self.pos,
+ "Item lookup of unicode character codes now always converts to a Unicode string. "
+ "Use an explicit C integer cast to get back the previous integer lookup behaviour.", level=1)
+ self.index = self.index.coerce_to_pyobject(env)
+ self.is_temp = 1
+ elif self.index.type.is_int and base_type is not dict_type:
if (getting
+ and not env.directives['boundscheck']
and (base_type in (list_type, tuple_type, bytearray_type))
and (not self.index.type.signed
or not env.directives['wraparound']
or (isinstance(self.index, IntNode) and
self.index.has_constant_result() and self.index.constant_result >= 0))
- and not env.directives['boundscheck']):
+ ):
self.is_temp = 0
else:
self.is_temp = 1
@@ -3493,7 +3925,7 @@
else:
# not using 'uchar' to enable fast and safe error reporting as '-1'
self.type = PyrexTypes.c_int_type
- elif is_slice and base_type in (bytes_type, str_type, unicode_type, list_type, tuple_type):
+ elif is_slice and base_type in (bytes_type, bytearray_type, str_type, unicode_type, list_type, tuple_type):
self.type = base_type
else:
item_type = None
@@ -3513,6 +3945,8 @@
def analyse_as_c_array(self, env, is_slice):
base_type = self.base.type
self.type = base_type.base_type
+ if self.type.is_cpp_class:
+ self.type = PyrexTypes.CReferenceType(self.type)
if is_slice:
self.type = base_type
elif self.index.type.is_pyobject:
@@ -3537,7 +3971,7 @@
if self.exception_check:
if not setting:
self.is_temp = True
- if self.exception_value is None:
+ if needs_cpp_exception_conversion(self):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
self.index = self.index.coerce_to(func_type.args[0].type, env)
self.type = func_type.return_type
@@ -3555,6 +3989,9 @@
if base_type.templates is None:
error(self.pos, "Can only parameterize template functions.")
self.type = error_type
+ elif self.type_indices is None:
+ # Error recorded earlier.
+ self.type = error_type
elif len(base_type.templates) != len(self.type_indices):
error(self.pos, "Wrong number of template arguments: expected %s, got %s" % (
(len(base_type.templates), len(self.type_indices))))
@@ -3591,25 +4028,45 @@
else:
indices = [self.index]
- base_type = self.base.type
+ base = self.base
+ base_type = base.type
replacement_node = None
if base_type.is_memoryviewslice:
# memoryviewslice indexing or slicing
from . import MemoryView
+ if base.is_memview_slice:
+ # For memory views, "view[i][j]" is the same as "view[i, j]" => use the latter for speed.
+ merged_indices = base.merged_indices(indices)
+ if merged_indices is not None:
+ base = base.base
+ base_type = base.type
+ indices = merged_indices
have_slices, indices, newaxes = MemoryView.unellipsify(indices, base_type.ndim)
if have_slices:
- replacement_node = MemoryViewSliceNode(self.pos, indices=indices, base=self.base)
+ replacement_node = MemoryViewSliceNode(self.pos, indices=indices, base=base)
else:
- replacement_node = MemoryViewIndexNode(self.pos, indices=indices, base=self.base)
-
- elif base_type.is_buffer and len(indices) == base_type.ndim:
- # Buffer indexing
- is_buffer_access = True
- indices = [index.analyse_types(env) for index in indices]
- if all(index.type.is_int for index in indices):
- replacement_node = BufferIndexNode(self.pos, indices=indices, base=self.base)
- # On cloning, indices is cloned. Otherwise, unpack index into indices.
- assert not isinstance(self.index, CloneNode)
+ replacement_node = MemoryViewIndexNode(self.pos, indices=indices, base=base)
+ elif base_type.is_buffer or base_type.is_pythran_expr:
+ if base_type.is_pythran_expr or len(indices) == base_type.ndim:
+ # Buffer indexing
+ is_buffer_access = True
+ indices = [index.analyse_types(env) for index in indices]
+ if base_type.is_pythran_expr:
+ do_replacement = all(
+ index.type.is_int or index.is_slice or index.type.is_pythran_expr
+ for index in indices)
+ if do_replacement:
+ for i,index in enumerate(indices):
+ if index.is_slice:
+ index = SliceIntNode(index.pos, start=index.start, stop=index.stop, step=index.step)
+ index = index.analyse_types(env)
+ indices[i] = index
+ else:
+ do_replacement = all(index.type.is_int for index in indices)
+ if do_replacement:
+ replacement_node = BufferIndexNode(self.pos, indices=indices, base=base)
+ # On cloning, indices is cloned. Otherwise, unpack index into indices.
+ assert not isinstance(self.index, CloneNode)
if replacement_node is not None:
replacement_node = replacement_node.analyse_types(env, getting)
@@ -3774,6 +4231,8 @@
if not self.is_temp:
# all handled in self.calculate_result_code()
return
+
+ utility_code = None
if self.type.is_pyobject:
error_value = 'NULL'
if self.index.type.is_int:
@@ -3783,32 +4242,38 @@
function = "__Pyx_GetItemInt_Tuple"
else:
function = "__Pyx_GetItemInt"
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c"))
+ utility_code = TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c")
else:
if self.base.type is dict_type:
function = "__Pyx_PyDict_GetItem"
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DictGetItem", "ObjectHandling.c"))
+ utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
+ elif self.base.type is py_object_type and self.index.type in (str_type, unicode_type):
+ # obj[str] is probably doing a dict lookup
+ function = "__Pyx_PyObject_Dict_GetItem"
+ utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
else:
- function = "PyObject_GetItem"
+ function = "__Pyx_PyObject_GetItem"
+ code.globalstate.use_utility_code(
+ TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c"))
+ utility_code = UtilityCode.load_cached("ObjectGetItem", "ObjectHandling.c")
elif self.type.is_unicode_char and self.base.type is unicode_type:
assert self.index.type.is_int
function = "__Pyx_GetItemInt_Unicode"
error_value = '(Py_UCS4)-1'
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetItemIntUnicode", "StringTools.c"))
+ utility_code = UtilityCode.load_cached("GetItemIntUnicode", "StringTools.c")
elif self.base.type is bytearray_type:
assert self.index.type.is_int
assert self.type.is_int
function = "__Pyx_GetItemInt_ByteArray"
error_value = '-1'
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c"))
+ utility_code = UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c")
elif not (self.base.type.is_cpp_class and self.exception_check):
assert False, "unexpected type %s and base type %s for indexing" % (
self.type, self.base.type)
+ if utility_code is not None:
+ code.globalstate.use_utility_code(utility_code)
+
if self.index.type.is_int:
index_code = self.index.result()
else:
@@ -3818,6 +4283,7 @@
translate_cpp_exception(code, self.pos,
"%s = %s[%s];" % (self.result(), self.base.result(),
self.index.result()),
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
@@ -3830,7 +4296,7 @@
self.extra_index_params(code),
code.error_goto_if(error_check % self.result(), self.pos)))
if self.type.is_pyobject:
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
def generate_setitem_code(self, value_code, code):
if self.index.type.is_int:
@@ -3866,7 +4332,7 @@
self.pos))
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
self.generate_subexpr_evaluation_code(code)
if self.type.is_pyobject:
@@ -3875,8 +4341,7 @@
value_code = self._check_byte_value(code, rhs)
self.generate_setitem_code(value_code, code)
elif self.base.type.is_cpp_class and self.exception_check and self.exception_check == '+':
- if overloaded_assignment and exception_check and \
- self.exception_value != exception_value:
+ if overloaded_assignment and exception_check and self.exception_value != exception_value:
# Handle the case that both the index operator and the assignment
# operator have a c++ exception handler and they are not the same.
translate_double_cpp_exception(code, self.pos, self.type,
@@ -3888,6 +4353,7 @@
# both exception handlers are the same.
translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), rhs.result()),
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln(
@@ -3976,6 +4442,9 @@
# Whether we're assigning to a buffer (in that case it needs to be writable)
writable_needed = False
+ # Any indexing temp variables that we need to clean up.
+ index_temps = ()
+
def analyse_target_types(self, env):
self.analyse_types(env, getting=False)
@@ -3985,7 +4454,7 @@
indexing and slicing subclasses
"""
# self.indices are already analyzed
- if not self.base.is_name:
+ if not self.base.is_name and not is_pythran_expr(self.base.type):
error(self.pos, "Can only index buffer variables")
self.type = error_type
return self
@@ -4004,11 +4473,15 @@
return self
def analyse_buffer_index(self, env, getting):
- self.base = self.base.coerce_to_simple(env)
- self.type = self.base.type.dtype
+ if is_pythran_expr(self.base.type):
+ index_with_type_list = [(idx, idx.type) for idx in self.indices]
+ self.type = PythranExpr(pythran_indexing_type(self.base.type, index_with_type_list))
+ else:
+ self.base = self.base.coerce_to_simple(env)
+ self.type = self.base.type.dtype
self.buffer_type = self.base.type
- if getting and self.type.is_pyobject:
+ if getting and (self.type.is_pyobject or self.type.is_pythran_expr):
self.is_temp = True
def analyse_assignment(self, rhs):
@@ -4024,10 +4497,6 @@
def nogil_check(self, env):
if self.is_buffer_access or self.is_memview_index:
- if env.directives['boundscheck']:
- warning(self.pos, "Use boundscheck(False) for faster access",
- level=1)
-
if self.type.is_pyobject:
error(self.pos, "Cannot access buffer with object dtype without gil")
self.type = error_type
@@ -4041,20 +4510,26 @@
base = base.arg
return base.type.get_entry(base)
+ def get_index_in_temp(self, code, ivar):
+ ret = code.funcstate.allocate_temp(
+ PyrexTypes.widest_numeric_type(
+ ivar.type,
+ PyrexTypes.c_ssize_t_type if ivar.type.signed else PyrexTypes.c_size_t_type),
+ manage_ref=False)
+ code.putln("%s = %s;" % (ret, ivar.result()))
+ return ret
+
def buffer_lookup_code(self, code):
"""
ndarray[1, 2, 3] and memslice[1, 2, 3]
"""
- # Assign indices to temps of at least (s)size_t to allow further index calculations.
- index_temps = [
- code.funcstate.allocate_temp(
- PyrexTypes.widest_numeric_type(
- ivar.type, PyrexTypes.c_ssize_t_type if ivar.type.signed else PyrexTypes.c_size_t_type),
- manage_ref=False)
- for ivar in self.indices]
+ if self.in_nogil_context:
+ if self.is_buffer_access or self.is_memview_index:
+ if code.globalstate.directives['boundscheck']:
+ warning(self.pos, "Use boundscheck(False) for faster access", level=1)
- for temp, index in zip(index_temps, self.indices):
- code.putln("%s = %s;" % (temp, index.result()))
+ # Assign indices to temps of at least (s)size_t to allow further index calculations.
+ self.index_temps = index_temps = [self.get_index_in_temp(code,ivar) for ivar in self.indices]
# Generate buffer access code using these temps
from . import Buffer
@@ -4082,6 +4557,27 @@
rhs.free_temps(code)
def generate_buffer_setitem_code(self, rhs, code, op=""):
+ base_type = self.base.type
+ if is_pythran_expr(base_type) and is_pythran_supported_type(rhs.type):
+ obj = code.funcstate.allocate_temp(PythranExpr(pythran_type(self.base.type)), manage_ref=False)
+ # We have got to do this because we have to declare pythran objects
+ # at the beginning of the functions.
+ # Indeed, Cython uses "goto" statement for error management, and
+ # RAII doesn't work with that kind of construction.
+ # Moreover, the way Pythran expressions are made is that they don't
+ # support move-assignation easily.
+ # This, we explicitly destroy then in-place new objects in this
+ # case.
+ code.putln("__Pyx_call_destructor(%s);" % obj)
+ code.putln("new (&%s) decltype(%s){%s};" % (obj, obj, self.base.pythran_result()))
+ code.putln("%s%s %s= %s;" % (
+ obj,
+ pythran_indexing_code(self.indices),
+ op,
+ rhs.pythran_result()))
+ code.funcstate.release_temp(obj)
+ return
+
# Used from generate_assignment_code and InPlaceAssignmentNode
buffer_entry, ptrexpr = self.buffer_lookup_code(code)
@@ -4092,17 +4588,26 @@
manage_ref=False)
rhs_code = rhs.result()
code.putln("%s = %s;" % (ptr, ptrexpr))
- code.put_gotref("*%s" % ptr)
+ code.put_gotref("*%s" % ptr, self.buffer_type.dtype)
code.putln("__Pyx_INCREF(%s); __Pyx_DECREF(*%s);" % (
rhs_code, ptr))
code.putln("*%s %s= %s;" % (ptr, op, rhs_code))
- code.put_giveref("*%s" % ptr)
+ code.put_giveref("*%s" % ptr, self.buffer_type.dtype)
code.funcstate.release_temp(ptr)
else:
# Simple case
code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result()))
def generate_result_code(self, code):
+ if is_pythran_expr(self.base.type):
+ res = self.result()
+ code.putln("__Pyx_call_destructor(%s);" % res)
+ code.putln("new (&%s) decltype(%s){%s%s};" % (
+ res,
+ res,
+ self.base.pythran_result(),
+ pythran_indexing_code(self.indices)))
+ return
buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
if self.type.is_pyobject:
# is_temp is True, so must pull out value and incref it.
@@ -4111,6 +4616,12 @@
code.putln("%s = (PyObject *) *%s;" % (self.result(), self.buffer_ptr_code))
code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result())
+ def free_subexpr_temps(self, code):
+ for temp in self.index_temps:
+ code.funcstate.release_temp(temp)
+ self.index_temps = ()
+ super(BufferIndexNode, self).free_subexpr_temps(code)
+
class MemoryViewIndexNode(BufferIndexNode):
@@ -4122,9 +4633,15 @@
# memoryviewslice indexing or slicing
from . import MemoryView
+ self.is_pythran_mode = has_np_pythran(env)
indices = self.indices
have_slices, indices, newaxes = MemoryView.unellipsify(indices, self.base.type.ndim)
+ if not getting:
+ self.writable_needed = True
+ if self.base.is_name or self.base.is_attribute:
+ self.base.entry.type.writable_needed = True
+
self.memslice_index = (not newaxes and len(indices) == self.base.type.ndim)
axes = []
@@ -4272,6 +4789,37 @@
else:
return MemoryCopySlice(self.pos, self)
+ def merged_indices(self, indices):
+ """Return a new list of indices/slices with 'indices' merged into the current ones
+ according to slicing rules.
+ Is used to implement "view[i][j]" => "view[i, j]".
+ Return None if the indices cannot (easily) be merged at compile time.
+ """
+ if not indices:
+ return None
+ # NOTE: Need to evaluate "self.original_indices" here as they might differ from "self.indices".
+ new_indices = self.original_indices[:]
+ indices = indices[:]
+ for i, s in enumerate(self.original_indices):
+ if s.is_slice:
+ if s.start.is_none and s.stop.is_none and s.step.is_none:
+ # Full slice found, replace by index.
+ new_indices[i] = indices[0]
+ indices.pop(0)
+ if not indices:
+ return new_indices
+ else:
+ # Found something non-trivial, e.g. a partial slice.
+ return None
+ elif not s.type.is_int:
+ # Not a slice, not an integer index => could be anything...
+ return None
+ if indices:
+ if len(new_indices) + len(indices) > self.base.type.ndim:
+ return None
+ new_indices += indices
+ return new_indices
+
def is_simple(self):
if self.is_ellipsis_noop:
# TODO: fix SimpleCallNode.is_simple()
@@ -4307,7 +4855,7 @@
assert not list(it)
buffer_entry.generate_buffer_slice_code(
- code, self.original_indices, self.result(),
+ code, self.original_indices, self.result(), self.type,
have_gil=have_gil, have_slices=have_slices,
directives=code.globalstate.directives)
@@ -4349,6 +4897,7 @@
self.dst.generate_evaluation_code(code)
self._generate_assignment_code(rhs, code)
self.dst.generate_disposal_code(code)
+ self.dst.free_temps(code)
rhs.generate_disposal_code(code)
rhs.free_temps(code)
@@ -4409,8 +4958,17 @@
code.putln("%s __pyx_temp_slice = %s;" % (slice_decl, self.dst.result()))
dst_temp = "__pyx_temp_slice"
+ force_strided = False
+ indices = self.dst.original_indices
+ for idx in indices:
+ if isinstance(idx, SliceNode) and not (idx.start.is_none and
+ idx.stop.is_none and
+ idx.step.is_none):
+ force_strided = True
+
slice_iter_obj = MemoryView.slice_iter(self.dst.type, dst_temp,
- self.dst.type.ndim, code)
+ self.dst.type.ndim, code,
+ force_strided=force_strided)
p = slice_iter_obj.start_loops()
if dtype.is_pyobject:
@@ -4443,7 +5001,7 @@
return bytes_type
elif base_type.is_pyunicode_ptr:
return unicode_type
- elif base_type in (bytes_type, str_type, unicode_type,
+ elif base_type in (bytes_type, bytearray_type, str_type, unicode_type,
basestring_type, list_type, tuple_type):
return base_type
elif base_type.is_ptr or base_type.is_array:
@@ -4508,13 +5066,13 @@
def analyse_types(self, env, getting=True):
self.base = self.base.analyse_types(env)
- if self.base.type.is_memoryviewslice:
+ if self.base.type.is_buffer or self.base.type.is_pythran_expr or self.base.type.is_memoryviewslice:
none_node = NoneNode(self.pos)
index = SliceNode(self.pos,
start=self.start or none_node,
stop=self.stop or none_node,
step=none_node)
- index_node = IndexNode(self.pos, index, base=self.base)
+ index_node = IndexNode(self.pos, index=index, base=self.base)
return index_node.analyse_base_and_index_types(
env, getting=getting, setting=not getting,
analyse_base=False)
@@ -4566,13 +5124,59 @@
).analyse_types(env)
else:
c_int = PyrexTypes.c_py_ssize_t_type
+
+ def allow_none(node, default_value, env):
+ # Coerce to Py_ssize_t, but allow None as meaning the default slice bound.
+ from .UtilNodes import EvalWithTempExprNode, ResultRefNode
+
+ node_ref = ResultRefNode(node)
+ new_expr = CondExprNode(
+ node.pos,
+ true_val=IntNode(
+ node.pos,
+ type=c_int,
+ value=default_value,
+ constant_result=int(default_value) if default_value.isdigit() else not_a_constant,
+ ),
+ false_val=node_ref.coerce_to(c_int, env),
+ test=PrimaryCmpNode(
+ node.pos,
+ operand1=node_ref,
+ operator='is',
+ operand2=NoneNode(node.pos),
+ ).analyse_types(env)
+ ).analyse_result_type(env)
+ return EvalWithTempExprNode(node_ref, new_expr)
+
if self.start:
+ if self.start.type.is_pyobject:
+ self.start = allow_none(self.start, '0', env)
self.start = self.start.coerce_to(c_int, env)
if self.stop:
+ if self.stop.type.is_pyobject:
+ self.stop = allow_none(self.stop, 'PY_SSIZE_T_MAX', env)
self.stop = self.stop.coerce_to(c_int, env)
self.is_temp = 1
return self
+ def analyse_as_type(self, env):
+ base_type = self.base.analyse_as_type(env)
+ if base_type and not base_type.is_pyobject:
+ if not self.start and not self.stop:
+ # memory view
+ from . import MemoryView
+ env.use_utility_code(MemoryView.view_utility_code)
+ none_node = NoneNode(self.pos)
+ slice_node = SliceNode(
+ self.pos,
+ start=none_node,
+ stop=none_node,
+ step=none_node,
+ )
+ return PyrexTypes.MemoryViewSliceType(
+ base_type, MemoryView.get_axes_specs(env, [slice_node]))
+ return None
+
nogil_check = Node.gil_error
gil_message = "Slicing Python object"
@@ -4697,15 +5301,14 @@
start_code,
stop_code,
code.error_goto_if_null(result, self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
self.generate_subexpr_evaluation_code(code)
if self.type.is_pyobject:
code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
+ has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice = self.get_slice_config()
code.put_error_if_neg(self.pos,
"__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
self.base.py_result(),
@@ -4918,8 +5521,11 @@
def generate_result_code(self, code):
if self.is_literal:
- self.result_code = code.get_py_const(py_object_type, 'slice', cleanup_level=2)
- code = code.get_cached_constants_writer()
+ dedup_key = make_dedup_key(self.type, (self,))
+ self.result_code = code.get_py_const(py_object_type, 'slice', cleanup_level=2, dedup_key=dedup_key)
+ code = code.get_cached_constants_writer(self.result_code)
+ if code is None:
+ return # already initialised
code.mark_pos(self.pos)
code.putln(
@@ -4929,9 +5535,63 @@
self.stop.py_result(),
self.step.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
if self.is_literal:
- code.put_giveref(self.py_result())
+ self.generate_giveref(code)
+
+class SliceIntNode(SliceNode):
+ # start:stop:step in subscript list
+ # This is just a node to hold start,stop and step nodes that can be
+ # converted to integers. This does not generate a slice python object.
+ #
+ # start ExprNode
+ # stop ExprNode
+ # step ExprNode
+
+ is_temp = 0
+
+ def calculate_constant_result(self):
+ self.constant_result = slice(
+ self.start.constant_result,
+ self.stop.constant_result,
+ self.step.constant_result)
+
+ def compile_time_value(self, denv):
+ start = self.start.compile_time_value(denv)
+ stop = self.stop.compile_time_value(denv)
+ step = self.step.compile_time_value(denv)
+ try:
+ return slice(start, stop, step)
+ except Exception as e:
+ self.compile_time_value_error(e)
+
+ def may_be_none(self):
+ return False
+
+ def analyse_types(self, env):
+ self.start = self.start.analyse_types(env)
+ self.stop = self.stop.analyse_types(env)
+ self.step = self.step.analyse_types(env)
+
+ if not self.start.is_none:
+ self.start = self.start.coerce_to_integer(env)
+ if not self.stop.is_none:
+ self.stop = self.stop.coerce_to_integer(env)
+ if not self.step.is_none:
+ self.step = self.step.coerce_to_integer(env)
+
+ if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
+ self.is_literal = True
+ self.is_temp = False
+ return self
+
+ def calculate_result_code(self):
+ pass
+
+ def generate_result_code(self, code):
+ for a in self.start,self.stop,self.step:
+ if isinstance(a, CloneNode):
+ a.arg.result()
class CallNode(ExprNode):
@@ -4940,6 +5600,7 @@
may_return_none = None
def infer_type(self, env):
+ # TODO(robertwb): Reduce redundancy with analyse_types.
function = self.function
func_type = function.infer_type(env)
if isinstance(function, NewExprNode):
@@ -4953,6 +5614,15 @@
if func_type.is_ptr:
func_type = func_type.base_type
if func_type.is_cfunction:
+ if getattr(self.function, 'entry', None) and hasattr(self, 'args'):
+ alternatives = self.function.entry.all_alternatives()
+ arg_types = [arg.infer_type(env) for arg in self.args]
+ func_entry = PyrexTypes.best_match(arg_types, alternatives)
+ if func_entry:
+ func_type = func_entry.type
+ if func_type.is_ptr:
+ func_type = func_type.base_type
+ return func_type.return_type
return func_type.return_type
elif func_type is type_type:
if function.is_name and function.entry and function.entry.type:
@@ -4964,6 +5634,9 @@
return PyrexTypes.c_double_type
elif function.entry.name in Builtin.types_that_construct_their_instance:
return result_type
+ func_type = self.function.analyse_as_type(env)
+ if func_type and (func_type.is_struct_or_union or func_type.is_cpp_class):
+ return func_type
return py_object_type
def type_dependencies(self, env):
@@ -4991,6 +5664,32 @@
return False
return ExprNode.may_be_none(self)
+ def set_py_result_type(self, function, func_type=None):
+ if func_type is None:
+ func_type = function.type
+ if func_type is Builtin.type_type and (
+ function.is_name and
+ function.entry and
+ function.entry.is_builtin and
+ function.entry.name in Builtin.types_that_construct_their_instance):
+ # calling a builtin type that returns a specific object type
+ if function.entry.name == 'float':
+ # the following will come true later on in a transform
+ self.type = PyrexTypes.c_double_type
+ self.result_ctype = PyrexTypes.c_double_type
+ else:
+ self.type = Builtin.builtin_types[function.entry.name]
+ self.result_ctype = py_object_type
+ self.may_return_none = False
+ elif function.is_name and function.type_entry:
+ # We are calling an extension type constructor. As long as we do not
+ # support __new__(), the result type is clear
+ self.type = function.type_entry.type
+ self.result_ctype = py_object_type
+ self.may_return_none = False
+ else:
+ self.type = py_object_type
+
def analyse_as_type_constructor(self, env):
type = self.function.analyse_as_type(env)
if type and type.is_struct_or_union:
@@ -5008,6 +5707,10 @@
elif type and type.is_cpp_class:
self.args = [ arg.analyse_types(env) for arg in self.args ]
constructor = type.scope.lookup("")
+ if not constructor:
+ error(self.function.pos, "no constructor found for C++ type '%s'" % self.function.name)
+ self.type = error_type
+ return self
self.function = RawCNameExprNode(self.function.pos, constructor.type)
self.function.entry = constructor
self.function.set_cname(type.empty_declaration_code())
@@ -5022,7 +5725,7 @@
func_type = self.function_type()
if func_type.is_pyobject:
self.gil_error()
- elif not getattr(func_type, 'nogil', False):
+ elif not func_type.is_error and not getattr(func_type, 'nogil', False):
self.gil_error()
gil_message = "Calling gil-requiring function"
@@ -5049,6 +5752,7 @@
has_optional_args = False
nogil = False
analysed = False
+ overflowcheck = False
def compile_time_value(self, denv):
function = self.function.compile_time_value(denv)
@@ -5058,6 +5762,16 @@
except Exception as e:
self.compile_time_value_error(e)
+ @classmethod
+ def for_cproperty(cls, pos, obj, entry):
+ # Create a call node for C property access.
+ property_scope = entry.scope
+ getter_entry = property_scope.lookup_here(entry.name)
+ assert getter_entry, "Getter not found in scope %s: %s" % (property_scope, property_scope.entries)
+ function = NameNode(pos, name=entry.name, entry=getter_entry, type=getter_entry.type)
+ node = cls(pos, function=function, args=[obj])
+ return node
+
def analyse_as_type(self, env):
attr = self.function.as_cython_attribute()
if attr == 'pointer':
@@ -5069,6 +5783,11 @@
error(self.args[0].pos, "Unknown type")
else:
return PyrexTypes.CPtrType(type)
+ elif attr == 'typeof':
+ if len(self.args) != 1:
+ error(self.args.pos, "only one type allowed.")
+ operand = self.args[0].analyse_types(env)
+ return operand.type
def explicit_args_kwds(self):
return self.args, None
@@ -5090,38 +5809,35 @@
function.obj = CloneNode(self.self)
func_type = self.function_type()
- if func_type.is_pyobject:
+ self.is_numpy_call_with_exprs = False
+ if (has_np_pythran(env) and function.is_numpy_attribute and
+ pythran_is_numpy_func_supported(function)):
+ has_pythran_args = True
+ self.arg_tuple = TupleNode(self.pos, args = self.args)
+ self.arg_tuple = self.arg_tuple.analyse_types(env)
+ for arg in self.arg_tuple.args:
+ has_pythran_args &= is_pythran_supported_node_or_none(arg)
+ self.is_numpy_call_with_exprs = bool(has_pythran_args)
+ if self.is_numpy_call_with_exprs:
+ env.add_include_file(pythran_get_func_include_file(function))
+ return NumPyMethodCallNode.from_node(
+ self,
+ function_cname=pythran_functor(function),
+ arg_tuple=self.arg_tuple,
+ type=PythranExpr(pythran_func_type(function, self.arg_tuple.args)),
+ )
+ elif func_type.is_pyobject:
self.arg_tuple = TupleNode(self.pos, args = self.args)
self.arg_tuple = self.arg_tuple.analyse_types(env).coerce_to_pyobject(env)
self.args = None
- if func_type is Builtin.type_type and function.is_name and \
- function.entry and \
- function.entry.is_builtin and \
- function.entry.name in Builtin.types_that_construct_their_instance:
- # calling a builtin type that returns a specific object type
- if function.entry.name == 'float':
- # the following will come true later on in a transform
- self.type = PyrexTypes.c_double_type
- self.result_ctype = PyrexTypes.c_double_type
- else:
- self.type = Builtin.builtin_types[function.entry.name]
- self.result_ctype = py_object_type
- self.may_return_none = False
- elif function.is_name and function.type_entry:
- # We are calling an extension type constructor. As
- # long as we do not support __new__(), the result type
- # is clear
- self.type = function.type_entry.type
- self.result_ctype = py_object_type
- self.may_return_none = False
- else:
- self.type = py_object_type
+ self.set_py_result_type(function, func_type)
self.is_temp = 1
else:
self.args = [ arg.analyse_types(env) for arg in self.args ]
self.analyse_c_function_call(env)
if func_type.exception_check == '+':
self.is_temp = True
+
return self
def function_type(self):
@@ -5173,7 +5889,8 @@
else:
alternatives = overloaded_entry.all_alternatives()
- entry = PyrexTypes.best_match(args, alternatives, self.pos, env)
+ entry = PyrexTypes.best_match([arg.type for arg in args],
+ alternatives, self.pos, env, args)
if not entry:
self.type = PyrexTypes.error_type
@@ -5209,7 +5926,7 @@
if formal_arg.not_none:
if self.self:
self.self = self.self.as_none_safe_node(
- "'NoneType' object has no attribute '%s'",
+ "'NoneType' object has no attribute '%{0}s'".format('.30' if len(entry.name) <= 30 else ''),
error='PyExc_AttributeError',
format_args=[entry.name])
else:
@@ -5235,8 +5952,6 @@
for i in range(min(max_nargs, actual_nargs)):
formal_arg = func_type.args[i]
formal_type = formal_arg.type
- if formal_type.is_const:
- formal_type = formal_type.const_base_type
arg = args[i].coerce_to(formal_type, env)
if formal_arg.not_none:
# C methods must do the None checks at *call* time
@@ -5258,7 +5973,7 @@
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
- if i > 0: # first argument doesn't matter
+ if i > 0: # first argument doesn't matter
some_args_in_temps = True
arg = arg.coerce_to_temp(env)
args[i] = arg
@@ -5287,7 +6002,7 @@
# case)
for i in range(actual_nargs-1):
if i == 0 and self.self is not None:
- continue # self is ok
+ continue # self is ok
arg = args[i]
if arg.nonlocally_immutable():
# locals, C functions, unassignable types are safe.
@@ -5303,7 +6018,7 @@
else:
#self.args[i] = arg.coerce_to_temp(env)
# instead: issue a warning
- if i > 0 or i == 1 and self.self is not None: # skip first arg
+ if i > 0 or i == 1 and self.self is not None: # skip first arg
warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
break
@@ -5334,15 +6049,13 @@
# Called in 'nogil' context?
self.nogil = env.nogil
- if (self.nogil and
- func_type.exception_check and
- func_type.exception_check != '+'):
- env.use_utility_code(pyerr_occurred_withgil_utility_code)
# C++ exception handler
if func_type.exception_check == '+':
- if func_type.exception_value is None:
+ if needs_cpp_exception_conversion(func_type):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
+ self.overflowcheck = env.directives['overflowcheck']
+
def calculate_result_code(self):
return self.c_call_code()
@@ -5357,8 +6070,8 @@
expected_nargs = max_nargs - func_type.optional_arg_count
actual_nargs = len(self.args)
for formal_arg, actual_arg in args[:expected_nargs]:
- arg_code = actual_arg.result_as(formal_arg.type)
- arg_list_code.append(arg_code)
+ arg_code = actual_arg.move_result_rhs_as(formal_arg.type)
+ arg_list_code.append(arg_code)
if func_type.is_overridable:
arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
@@ -5371,7 +6084,7 @@
arg_list_code.append(optional_args)
for actual_arg in self.args[len(formal_args):]:
- arg_list_code.append(actual_arg.result())
+ arg_list_code.append(actual_arg.move_result_rhs())
result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code))
return result
@@ -5382,30 +6095,76 @@
return False # skip allocation of unused result temp
return True
+ def generate_evaluation_code(self, code):
+ function = self.function
+ if function.is_name or function.is_attribute:
+ code.globalstate.use_entry_utility_code(function.entry)
+
+ abs_function_cnames = ('abs', 'labs', '__Pyx_abs_longlong')
+ is_signed_int = self.type.is_int and self.type.signed
+ if self.overflowcheck and is_signed_int and function.result() in abs_function_cnames:
+ code.globalstate.use_utility_code(UtilityCode.load_cached("Common", "Overflow.c"))
+ code.putln('if (unlikely(%s == __PYX_MIN(%s))) {\
+ PyErr_SetString(PyExc_OverflowError,\
+ "Trying to take the absolute value of the most negative integer is not defined."); %s; }' % (
+ self.args[0].result(),
+ self.args[0].type.empty_declaration_code(),
+ code.error_goto(self.pos)))
+
+ if not function.type.is_pyobject or len(self.arg_tuple.args) > 1 or (
+ self.arg_tuple.args and self.arg_tuple.is_literal):
+ super(SimpleCallNode, self).generate_evaluation_code(code)
+ return
+
+ # Special case 0-args and try to avoid explicit tuple creation for Python calls with 1 arg.
+ arg = self.arg_tuple.args[0] if self.arg_tuple.args else None
+ subexprs = (self.self, self.coerced_self, function, arg)
+ for subexpr in subexprs:
+ if subexpr is not None:
+ subexpr.generate_evaluation_code(code)
+
+ code.mark_pos(self.pos)
+ assert self.is_temp
+ self.allocate_temp_result(code)
+
+ if arg is None:
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCallNoArg", "ObjectHandling.c"))
+ code.putln(
+ "%s = __Pyx_PyObject_CallNoArg(%s); %s" % (
+ self.result(),
+ function.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ else:
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCallOneArg", "ObjectHandling.c"))
+ code.putln(
+ "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % (
+ self.result(),
+ function.py_result(),
+ arg.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+
+ self.generate_gotref(code)
+
+ for subexpr in subexprs:
+ if subexpr is not None:
+ subexpr.generate_disposal_code(code)
+ subexpr.free_temps(code)
+
def generate_result_code(self, code):
func_type = self.function_type()
- if self.function.is_name or self.function.is_attribute:
- code.globalstate.use_entry_utility_code(self.function.entry)
if func_type.is_pyobject:
- if func_type is not type_type and not self.arg_tuple.args and self.arg_tuple.is_literal:
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCallNoArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallNoArg(%s); %s" % (
- self.result(),
- self.function.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- arg_code = self.arg_tuple.py_result()
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- self.function.py_result(),
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ arg_code = self.arg_tuple.py_result()
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCall", "ObjectHandling.c"))
+ code.putln(
+ "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
+ self.result(),
+ self.function.py_result(),
+ arg_code,
+ code.error_goto_if_null(self.result(), self.pos)))
+ self.generate_gotref(code)
elif func_type.is_cfunction:
if self.has_optional_args:
actual_nargs = len(self.args)
@@ -5428,13 +6187,15 @@
elif self.type.is_memoryviewslice:
assert self.is_temp
exc_checks.append(self.type.error_condition(self.result()))
- else:
+ elif func_type.exception_check != '+':
exc_val = func_type.exception_value
exc_check = func_type.exception_check
if exc_val is not None:
- exc_checks.append("%s == %s" % (self.result(), exc_val))
+ exc_checks.append("%s == %s" % (self.result(), func_type.return_type.cast_code(exc_val)))
if exc_check:
if self.nogil:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("ErrOccurredWithGIL", "Exceptions.c"))
exc_checks.append("__Pyx_ErrOccurredWithGIL()")
else:
exc_checks.append("PyErr_Occurred()")
@@ -5451,6 +6212,7 @@
lhs = ""
if func_type.exception_check == '+':
translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
+ self.result() if self.type.is_pyobject else None,
func_type.exception_value, self.nogil)
else:
if exc_checks:
@@ -5459,11 +6221,39 @@
goto_error = ""
code.putln("%s%s; %s" % (lhs, rhs, goto_error))
if self.type.is_pyobject and self.result():
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
if self.has_optional_args:
code.funcstate.release_temp(self.opt_arg_struct)
+class NumPyMethodCallNode(ExprNode):
+ # Pythran call to a NumPy function or method.
+ #
+ # function_cname string the function/method to call
+ # arg_tuple TupleNode the arguments as an args tuple
+
+ subexprs = ['arg_tuple']
+ is_temp = True
+ may_return_none = True
+
+ def generate_evaluation_code(self, code):
+ code.mark_pos(self.pos)
+ self.allocate_temp_result(code)
+
+ assert self.arg_tuple.mult_factor is None
+ args = self.arg_tuple.args
+ for arg in args:
+ arg.generate_evaluation_code(code)
+
+ code.putln("// function evaluation code for numpy function")
+ code.putln("__Pyx_call_destructor(%s);" % self.result())
+ code.putln("new (&%s) decltype(%s){%s{}(%s)};" % (
+ self.result(),
+ self.result(),
+ self.function_cname,
+ ", ".join(a.pythran_result() for a in args)))
+
+
class PyMethodCallNode(SimpleCallNode):
# Specialised call to a (potential) PyMethodObject with non-constant argument tuple.
# Allows the self argument to be injected directly instead of repacking a tuple for it.
@@ -5497,10 +6287,8 @@
self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
code.putln("%s = NULL;" % self_arg)
- arg_offset_cname = None
- if len(args) > 1:
- arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % arg_offset_cname)
+ arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
+ code.putln("%s = 0;" % arg_offset_cname)
def attribute_is_likely_method(attr):
obj = attr.obj
@@ -5514,7 +6302,7 @@
# not an attribute itself, but might have been assigned from one (e.g. bound method)
for assignment in self.function.cf_state:
value = assignment.rhs
- if value and value.is_attribute and value.obj.type.is_pyobject:
+ if value and value.is_attribute and value.obj.type and value.obj.type.is_pyobject:
if attribute_is_likely_method(value):
likely_method = 'likely'
break
@@ -5532,122 +6320,35 @@
code.put_incref(self_arg, py_object_type)
code.put_incref("function", py_object_type)
# free method object as early to possible to enable reuse from CPython's freelist
- code.put_decref_set(function, "function")
- if len(args) > 1:
- code.putln("%s = 1;" % arg_offset_cname)
+ code.put_decref_set(function, py_object_type, "function")
+ code.putln("%s = 1;" % arg_offset_cname)
code.putln("}")
code.putln("}")
- if not args:
- # fastest special case: try to avoid tuple creation
- code.putln("if (%s) {" % self_arg)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % (
- self.result(),
- function, self_arg,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_decref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
- code.putln("} else {")
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallNoArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallNoArg(%s); %s" % (
- self.result(),
- function,
- code.error_goto_if_null(self.result(), self.pos)))
- code.putln("}")
- code.put_gotref(self.py_result())
- else:
- if len(args) == 1:
- code.putln("if (!%s) {" % self_arg)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- arg = args[0]
- code.putln(
- "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % (
- self.result(),
- function, arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- arg.generate_disposal_code(code)
- code.put_gotref(self.py_result())
- code.putln("} else {")
- arg_offset = 1
- else:
- arg_offset = arg_offset_cname
-
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyFunctionFastCall", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyCFunctionFastCall", "ObjectHandling.c"))
- for test_func, call_prefix in [('PyFunction_Check', 'Py'), ('__Pyx_PyFastCFunction_Check', 'PyC')]:
- code.putln("#if CYTHON_FAST_%sCALL" % call_prefix.upper())
- code.putln("if (%s(%s)) {" % (test_func, function))
- code.putln("PyObject *%s[%d] = {%s, %s};" % (
- Naming.quick_temp_cname,
- len(args)+1,
- self_arg,
- ', '.join(arg.py_result() for arg in args)))
- code.putln("%s = __Pyx_%sFunction_FastCall(%s, %s+1-%s, %d+%s); %s" % (
- self.result(),
- call_prefix,
- function,
- Naming.quick_temp_cname,
- arg_offset,
- len(args),
- arg_offset,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.put_gotref(self.py_result())
- for arg in args:
- arg.generate_disposal_code(code)
- code.putln("} else")
- code.putln("#endif")
-
- code.putln("{")
- args_tuple = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = PyTuple_New(%d+%s); %s" % (
- args_tuple, len(args), arg_offset,
- code.error_goto_if_null(args_tuple, self.pos)))
- code.put_gotref(args_tuple)
-
- if len(args) > 1:
- code.putln("if (%s) {" % self_arg)
- code.putln("__Pyx_GIVEREF(%s); PyTuple_SET_ITEM(%s, 0, %s); %s = NULL;" % (
- self_arg, args_tuple, self_arg, self_arg)) # stealing owned ref in this case
- code.funcstate.release_temp(self_arg)
- if len(args) > 1:
- code.putln("}")
+ # actually call the function
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c"))
- for i, arg in enumerate(args):
- arg.make_owned_reference(code)
- code.put_giveref(arg.py_result())
- code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % (
- args_tuple, i, arg_offset, arg.py_result()))
- if len(args) > 1:
- code.funcstate.release_temp(arg_offset_cname)
-
- for arg in args:
- arg.generate_post_assignment_code(code)
- arg.free_temps(code)
-
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- function, args_tuple,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- code.put_decref_clear(args_tuple, py_object_type)
- code.funcstate.release_temp(args_tuple)
-
- if len(args) == 1:
- code.putln("}")
- code.putln("}") # !CYTHON_FAST_PYCALL
+ code.putln("{")
+ code.putln("PyObject *__pyx_callargs[%d] = {%s, %s};" % (
+ len(args)+1,
+ self_arg,
+ ', '.join(arg.py_result() for arg in args)))
+ code.putln("%s = __Pyx_PyObject_FastCall(%s, __pyx_callargs+1-%s, %d+%s);" % (
+ self.result(),
+ function,
+ arg_offset_cname,
+ len(args),
+ arg_offset_cname))
+
+ code.put_xdecref_clear(self_arg, py_object_type)
+ code.funcstate.release_temp(self_arg)
+ code.funcstate.release_temp(arg_offset_cname)
+ for arg in args:
+ arg.generate_disposal_code(code)
+ arg.free_temps(code)
+ code.putln(code.error_goto_if_null(self.result(), self.pos))
+ self.generate_gotref(code)
if reuse_function_temp:
self.function.generate_disposal_code(code)
@@ -5655,6 +6356,7 @@
else:
code.put_decref_clear(function, py_object_type)
code.funcstate.release_temp(function)
+ code.putln("}")
class InlinedDefNodeCallNode(CallNode):
@@ -5705,7 +6407,7 @@
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
- if i > 0: # first argument doesn't matter
+ if i > 0: # first argument doesn't matter
some_args_in_temps = True
arg = arg.coerce_to_temp(env)
self.args[i] = arg
@@ -5752,7 +6454,7 @@
self.function.def_node.entry.pyfunc_cname,
arg_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class PythonCapiFunctionNode(ExprNode):
@@ -5794,6 +6496,37 @@
SimpleCallNode.__init__(self, pos, **kwargs)
+class CachedBuiltinMethodCallNode(CallNode):
+ # Python call to a method of a known Python builtin (only created in transforms)
+
+ subexprs = ['obj', 'args']
+ is_temp = True
+
+ def __init__(self, call_node, obj, method_name, args):
+ super(CachedBuiltinMethodCallNode, self).__init__(
+ call_node.pos,
+ obj=obj, method_name=method_name, args=args,
+ may_return_none=call_node.may_return_none,
+ type=call_node.type)
+
+ def may_be_none(self):
+ if self.may_return_none is not None:
+ return self.may_return_none
+ return ExprNode.may_be_none(self)
+
+ def generate_result_code(self, code):
+ type_cname = self.obj.type.cname
+ obj_cname = self.obj.py_result()
+ args = [arg.py_result() for arg in self.args]
+ call_code = code.globalstate.cached_unbound_method_call_code(
+ obj_cname, type_cname, self.method_name, args)
+ code.putln("%s = %s; %s" % (
+ self.result(), call_code,
+ code.error_goto_if_null(self.result(), self.pos)
+ ))
+ self.generate_gotref(code)
+
+
class GeneralCallNode(CallNode):
# General Python function call, including keyword,
# * and ** arguments.
@@ -5852,15 +6585,7 @@
self.positional_args = self.positional_args.analyse_types(env)
self.positional_args = \
self.positional_args.coerce_to_pyobject(env)
- function = self.function
- if function.is_name and function.type_entry:
- # We are calling an extension type constructor. As long
- # as we do not support __new__(), the result type is clear
- self.type = function.type_entry.type
- self.result_ctype = py_object_type
- self.may_return_none = False
- else:
- self.type = py_object_type
+ self.set_py_result_type(self.function)
self.is_temp = 1
return self
@@ -5890,7 +6615,7 @@
kwargs = self.keyword_args
declared_args = function_type.args
if entry.is_cmethod:
- declared_args = declared_args[1:] # skip 'self'
+ declared_args = declared_args[1:] # skip 'self'
if len(pos_args) > len(declared_args):
error(self.pos, "function call got too many positional arguments, "
@@ -5898,8 +6623,10 @@
len(pos_args)))
return None
- matched_args = set([ arg.name for arg in declared_args[:len(pos_args)]
- if arg.name ])
+ matched_args = {
+ arg.name for arg in declared_args[:len(pos_args)]
+ if arg.name
+ }
unmatched_args = declared_args[len(pos_args):]
matched_kwargs_count = 0
args = list(pos_args)
@@ -6017,7 +6744,7 @@
self.positional_args.py_result(),
kwargs,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class AsTupleNode(ExprNode):
@@ -6027,6 +6754,7 @@
# arg ExprNode
subexprs = ['arg']
+ is_temp = 1
def calculate_constant_result(self):
self.constant_result = tuple(self.arg.constant_result)
@@ -6043,7 +6771,6 @@
if self.arg.type is tuple_type:
return self.arg.as_none_safe_node("'NoneType' object is not iterable")
self.type = tuple_type
- self.is_temp = 1
return self
def may_be_none(self):
@@ -6053,12 +6780,13 @@
gil_message = "Constructing Python tuple"
def generate_result_code(self, code):
+ cfunc = "__Pyx_PySequence_Tuple" if self.arg.type in (py_object_type, tuple_type) else "PySequence_Tuple"
code.putln(
- "%s = PySequence_Tuple(%s); %s" % (
+ "%s = %s(%s); %s" % (
self.result(),
- self.arg.py_result(),
+ cfunc, self.arg.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class MergedDictNode(ExprNode):
@@ -6116,22 +6844,13 @@
return dict_type
def analyse_types(self, env):
- args = [
+ self.keyword_args = [
arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
# FIXME: CPython's error message starts with the runtime function name
'argument after ** must be a mapping, not NoneType')
for arg in self.keyword_args
]
- if len(args) == 1 and args[0].type is dict_type:
- # strip this intermediate node and use the bare dict
- arg = args[0]
- if arg.is_name and arg.entry.is_arg and len(arg.entry.cf_assignments) == 1:
- # passing **kwargs through to function call => allow NULL
- arg.allow_null = True
- return arg
-
- self.keyword_args = args
return self
def may_be_none(self):
@@ -6160,16 +6879,18 @@
self.result(),
item.py_result(),
code.error_goto_if_null(self.result(), item.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
item.generate_disposal_code(code)
if item.type is not dict_type:
code.putln('} else {')
- code.putln("%s = PyObject_CallFunctionObjArgs((PyObject*)&PyDict_Type, %s, NULL); %s" % (
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCallOneArg", "ObjectHandling.c"))
+ code.putln("%s = __Pyx_PyObject_CallOneArg((PyObject*)&PyDict_Type, %s); %s" % (
self.result(),
item.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
item.generate_disposal_code(code)
code.putln('}')
item.free_temps(code)
@@ -6241,7 +6962,6 @@
is_attribute = 1
subexprs = ['obj']
- type = PyrexTypes.error_type
entry = None
is_called = 0
needs_none_check = True
@@ -6298,7 +7018,11 @@
# FIXME: this is way too redundant with analyse_types()
node = self.analyse_as_cimported_attribute_node(env, target=False)
if node is not None:
- return node.entry.type
+ if node.entry.type and node.entry.type.is_cfunction:
+ # special-case - function converted to pointer
+ return PyrexTypes.CPtrType(node.entry.type)
+ else:
+ return node.entry.type
node = self.analyse_as_type_attribute(env)
if node is not None:
return node.entry.type
@@ -6309,10 +7033,14 @@
# builtin types cannot be inferred as C functions as
# that would prevent their use as bound methods
return py_object_type
+ elif self.entry and self.entry.is_cmethod:
+ # special case: bound methods should not be inferred
+ # as their unbound method types
+ return py_object_type
return self.type
def analyse_target_declaration(self, env):
- pass
+ self.is_target = True
def analyse_target_types(self, env):
node = self.analyse_types(env, target = 1)
@@ -6323,6 +7051,8 @@
return node
def analyse_types(self, env, target = 0):
+ if not self.type:
+ self.type = PyrexTypes.error_type # default value if it isn't analysed successfully
self.initialized_check = env.directives['initializedcheck']
node = self.analyse_as_cimported_attribute_node(env, target)
if node is None and not target:
@@ -6330,7 +7060,7 @@
if node is None:
node = self.analyse_as_ordinary_attribute_node(env, target)
assert node is not None
- if node.entry:
+ if (node.is_attribute or node.is_name) and node.entry:
node.entry.used = True
if node.is_attribute:
node.wrap_obj_in_nonecheck(env)
@@ -6348,6 +7078,10 @@
entry.is_cglobal or entry.is_cfunction
or entry.is_type or entry.is_const):
return self.as_name_node(env, entry, target)
+ if self.is_cimported_module_without_shadow(env):
+ # TODO: search for submodule
+ error(self.pos, "cimported module has no attribute '%s'" % self.attribute)
+ return self
return None
def analyse_as_type_attribute(self, env):
@@ -6368,42 +7102,56 @@
return None
ubcm_entry = entry
else:
- # Create a temporary entry describing the C method
- # as an ordinary function.
- if entry.func_cname and not hasattr(entry.type, 'op_arg_struct'):
- cname = entry.func_cname
- if entry.type.is_static_method or env.parent_scope.is_cpp_class_scope:
- ctype = entry.type
- elif type.is_cpp_class:
- error(self.pos, "%s not a static member of %s" % (entry.name, type))
- ctype = PyrexTypes.error_type
- else:
- # Fix self type.
- ctype = copy.copy(entry.type)
- ctype.args = ctype.args[:]
- ctype.args[0] = PyrexTypes.CFuncTypeArg('self', type, 'self', None)
- else:
- cname = "%s->%s" % (type.vtabptr_cname, entry.cname)
- ctype = entry.type
- ubcm_entry = Symtab.Entry(entry.name, cname, ctype)
- ubcm_entry.is_cfunction = 1
- ubcm_entry.func_cname = entry.func_cname
- ubcm_entry.is_unbound_cmethod = 1
+ ubcm_entry = self._create_unbound_cmethod_entry(type, entry, env)
+ ubcm_entry.overloaded_alternatives = [
+ self._create_unbound_cmethod_entry(type, overloaded_alternative, env)
+ for overloaded_alternative in entry.overloaded_alternatives
+ ]
return self.as_name_node(env, ubcm_entry, target=False)
- elif type.is_enum:
+ elif type.is_enum or type.is_cpp_enum:
if self.attribute in type.values:
- return self.as_name_node(env, env.lookup(self.attribute), target=False)
+ for entry in type.entry.enum_values:
+ if entry.name == self.attribute:
+ return self.as_name_node(env, entry, target=False)
+ else:
+ error(self.pos, "%s not a known value of %s" % (self.attribute, type))
else:
error(self.pos, "%s not a known value of %s" % (self.attribute, type))
return None
+ def _create_unbound_cmethod_entry(self, type, entry, env):
+ # Create a temporary entry describing the unbound C method in `entry`
+ # as an ordinary function.
+ if entry.func_cname and entry.type.op_arg_struct is None:
+ cname = entry.func_cname
+ if entry.type.is_static_method or (
+ env.parent_scope and env.parent_scope.is_cpp_class_scope):
+ ctype = entry.type
+ elif type.is_cpp_class:
+ error(self.pos, "%s not a static member of %s" % (entry.name, type))
+ ctype = PyrexTypes.error_type
+ else:
+ # Fix self type.
+ ctype = copy.copy(entry.type)
+ ctype.args = ctype.args[:]
+ ctype.args[0] = PyrexTypes.CFuncTypeArg('self', type, 'self', None)
+ else:
+ cname = "%s->%s" % (type.vtabptr_cname, entry.cname)
+ ctype = entry.type
+ ubcm_entry = Symtab.Entry(entry.name, cname, ctype)
+ ubcm_entry.is_cfunction = 1
+ ubcm_entry.func_cname = entry.func_cname
+ ubcm_entry.is_unbound_cmethod = 1
+ ubcm_entry.scope = entry.scope
+ return ubcm_entry
+
def analyse_as_type(self, env):
module_scope = self.obj.analyse_as_module(env)
if module_scope:
return module_scope.lookup_type(self.attribute)
if not self.obj.is_string_literal:
base_type = self.obj.analyse_as_type(env)
- if base_type and hasattr(base_type, 'scope') and base_type.scope is not None:
+ if base_type and getattr(base_type, 'scope', None) is not None:
return base_type.scope.lookup_type(self.attribute)
return None
@@ -6454,13 +7202,18 @@
self.result_ctype = py_object_type
elif target and self.obj.type.is_builtin_type:
error(self.pos, "Assignment to an immutable object field")
+ elif self.entry and self.entry.is_cproperty:
+ if not target:
+ return SimpleCallNode.for_cproperty(self.pos, self.obj, self.entry).analyse_types(env)
+ # TODO: implement writable C-properties?
+ error(self.pos, "Assignment to a read-only property")
#elif self.type.is_memoryviewslice and not target:
# self.is_temp = True
return self
def analyse_attribute(self, env, obj_type = None):
# Look up attribute and set self.type and self.member.
- immutable_obj = obj_type is not None # used during type inference
+ immutable_obj = obj_type is not None # used during type inference
self.is_py_attr = 0
self.member = self.attribute
if obj_type is None:
@@ -6510,7 +7263,10 @@
# fused function go through assignment synthesis
# (foo = pycfunction(foo_func_obj)) and need to go through
# regular Python lookup as well
- if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
+ if entry.is_cproperty:
+ self.type = entry.type
+ return
+ elif (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
self.type = entry.type
self.member = entry.cname
return
@@ -6533,13 +7289,19 @@
self.member = self.attribute
self.type = py_object_type
self.is_py_attr = 1
+
if not obj_type.is_pyobject and not obj_type.is_error:
- if obj_type.can_coerce_to_pyobject(env):
+ # Expose python methods for immutable objects.
+ if (obj_type.is_string or obj_type.is_cpp_string
+ or obj_type.is_buffer or obj_type.is_memoryviewslice
+ or obj_type.is_numeric
+ or (obj_type.is_ctuple and obj_type.can_coerce_to_pyobject(env))
+ or (obj_type.is_struct and obj_type.can_coerce_to_pyobject(env))):
if not immutable_obj:
self.obj = self.obj.coerce_to_pyobject(env)
elif (obj_type.is_cfunction and (self.obj.is_name or self.obj.is_attribute)
- and self.obj.entry.as_variable
- and self.obj.entry.as_variable.type.is_pyobject):
+ and self.obj.entry.as_variable
+ and self.obj.entry.as_variable.type.is_pyobject):
# might be an optimised builtin function => unpack it
if not immutable_obj:
self.obj = self.obj.coerce_to_pyobject(env)
@@ -6556,7 +7318,7 @@
format_args = ()
if (self.obj.type.is_extension_type and self.needs_none_check and not
self.is_py_attr):
- msg = "'NoneType' object has no attribute '%s'"
+ msg = "'NoneType' object has no attribute '%{0}s'".format('.30' if len(self.attribute) <= 30 else '')
format_args = (self.attribute,)
elif self.obj.type.is_memoryviewslice:
if self.is_memslice_transpose:
@@ -6578,6 +7340,9 @@
gil_message = "Accessing Python attribute"
+ def is_cimported_module_without_shadow(self, env):
+ return self.obj.is_cimported_module_without_shadow(env)
+
def is_simple(self):
if self.obj:
return self.result_in_temp() or self.obj.is_simple()
@@ -6597,9 +7362,14 @@
return NameNode.is_ephemeral(self)
def calculate_result_code(self):
- #print "AttributeNode.calculate_result_code:", self.member ###
- #print "...obj node =", self.obj, "code", self.obj.result() ###
- #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
+ result = self.calculate_access_code()
+ if self.entry and self.entry.is_cpp_optional and not self.is_target:
+ result = "(*%s)" % result
+ return result
+
+ def calculate_access_code(self):
+ # Does the job of calculate_result_code but doesn't dereference cpp_optionals
+ # Therefore allowing access to the holder variable
obj = self.obj
obj_code = obj.result_as(obj.type)
#print "...obj_code =", obj_code ###
@@ -6650,7 +7420,7 @@
self.obj.py_result(),
code.intern_identifier(self.attribute),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
elif self.type.is_memoryviewslice:
if self.is_memslice_transpose:
# transpose the slice
@@ -6661,10 +7431,11 @@
return
code.putln("%s = %s;" % (self.result(), self.obj.result()))
- code.put_incref_memoryviewslice(self.result(), have_gil=True)
+ code.put_incref_memoryviewslice(self.result(), self.type,
+ have_gil=True)
- T = "__pyx_memslice_transpose(&%s) == 0"
- code.putln(code.error_goto_if(T % self.result(), self.pos))
+ T = "__pyx_memslice_transpose(&%s)" % self.result()
+ code.putln(code.error_goto_if_neg(T, self.pos))
elif self.initialized_check:
code.putln(
'if (unlikely(!%s.memview)) {'
@@ -6672,6 +7443,14 @@
'"Memoryview is not initialized");'
'%s'
'}' % (self.result(), code.error_goto(self.pos)))
+ elif self.entry.is_cpp_optional and self.initialized_check:
+ if self.is_target:
+ undereferenced_result = self.result()
+ else:
+ assert not self.is_temp # calculate_access_code() only makes sense for non-temps
+ undereferenced_result = self.calculate_access_code()
+ unbound_check_code = self.type.cpp_optional_check_for_null_code(undereferenced_result)
+ code.put_error_if_unbound(self.pos, self.entry, unbound_check_code=unbound_check_code)
else:
# result_code contains what is needed, but we may need to insert
# a check and raise an exception
@@ -6684,15 +7463,12 @@
def generate_disposal_code(self, code):
if self.is_temp and self.type.is_memoryviewslice and self.is_memslice_transpose:
# mirror condition for putting the memview incref here:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=True)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
+ code.put_xdecref_clear(self.result(), self.type, have_gil=True)
else:
ExprNode.generate_disposal_code(self, code)
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
self.obj.generate_evaluation_code(code)
if self.is_py_attr:
code.globalstate.use_utility_code(
@@ -6709,12 +7485,14 @@
self.member.upper(),
self.obj.result_as(self.obj.type),
rhs.result_as(self.ctype())))
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
else:
select_code = self.result()
if self.type.is_pyobject and self.use_managed_ref:
rhs.make_owned_reference(code)
- code.put_giveref(rhs.py_result())
- code.put_gotref(select_code)
+ rhs.generate_giveref(code)
+ code.put_gotref(select_code, self.type)
code.put_decref(select_code, self.ctype())
elif self.type.is_memoryviewslice:
from . import MemoryView
@@ -6725,7 +7503,7 @@
code.putln(
"%s = %s;" % (
select_code,
- rhs.result_as(self.ctype())))
+ rhs.move_result_rhs_as(self.ctype())))
#rhs.result()))
rhs.generate_post_assignment_code(code)
rhs.free_temps(code)
@@ -6754,6 +7532,12 @@
style, text = 'c_attr', 'c attribute (%s)'
code.annotate(self.pos, AnnotationItem(style, text % self.type, size=len(self.attribute)))
+ def get_known_standard_library_import(self):
+ module_name = self.obj.get_known_standard_library_import()
+ if module_name:
+ return StringEncoding.EncodedString("%s.%s" % (module_name, self.attribute))
+ return None
+
#-------------------------------------------------------------------
#
@@ -6856,9 +7640,10 @@
arg = arg.analyse_types(env)
self.args[i] = arg.coerce_to_pyobject(env)
if self.mult_factor:
- self.mult_factor = self.mult_factor.analyse_types(env)
- if not self.mult_factor.type.is_int:
- self.mult_factor = self.mult_factor.coerce_to_pyobject(env)
+ mult_factor = self.mult_factor.analyse_types(env)
+ if not mult_factor.type.is_int:
+ mult_factor = mult_factor.coerce_to_pyobject(env)
+ self.mult_factor = mult_factor.coerce_to_simple(env)
self.is_temp = 1
# not setting self.type here, subtypes do this
return self
@@ -6960,7 +7745,7 @@
len(self.args),
', '.join(arg.py_result() for arg in self.args),
code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
+ code.put_gotref(target, py_object_type)
elif self.type.is_ctuple:
for i, arg in enumerate(self.args):
code.putln("%s.f%s = %s;" % (
@@ -6977,7 +7762,7 @@
code.putln("%s = %s(%s%s); %s" % (
target, create_func, arg_count, size_factor,
code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
+ code.put_gotref(target, py_object_type)
if c_mult:
# FIXME: can't use a temp variable here as the code may
@@ -7001,7 +7786,7 @@
arg = self.args[i]
if c_mult or not arg.result_in_temp():
code.put_incref(arg.result(), arg.ctype())
- code.put_giveref(arg.py_result())
+ arg.generate_giveref(code)
code.putln("%s(%s, %s, %s);" % (
set_item_func,
target,
@@ -7018,7 +7803,7 @@
Naming.quick_temp_cname, target, mult_factor.py_result(),
code.error_goto_if_null(Naming.quick_temp_cname, self.pos)
))
- code.put_gotref(Naming.quick_temp_cname)
+ code.put_gotref(Naming.quick_temp_cname, py_object_type)
code.put_decref(target, py_object_type)
code.putln('%s = %s;' % (target, Naming.quick_temp_cname))
code.putln('}')
@@ -7040,7 +7825,7 @@
self.mult_factor.generate_disposal_code(code)
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
+ exception_check=None, exception_value=None):
if self.starred_assignment:
self.generate_starred_assignment_code(rhs, code)
else:
@@ -7102,17 +7887,16 @@
code.putln("PyObject* sequence = %s;" % rhs.py_result())
# list/tuple => check size
- code.putln("#if !CYTHON_COMPILING_IN_PYPY")
- code.putln("Py_ssize_t size = Py_SIZE(sequence);")
- code.putln("#else")
- code.putln("Py_ssize_t size = PySequence_Size(sequence);") # < 0 => exception
- code.putln("#endif")
+ code.putln("Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);")
code.putln("if (unlikely(size != %d)) {" % len(self.args))
- code.globalstate.use_utility_code(raise_too_many_values_to_unpack)
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c"))
code.putln("if (size > %d) __Pyx_RaiseTooManyValuesError(%d);" % (
len(self.args), len(self.args)))
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c"))
code.putln("else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);")
+ # < 0 => exception
code.putln(code.error_goto(self.pos))
code.putln("}")
@@ -7139,7 +7923,7 @@
code.putln("%s = PySequence_ITEM(sequence, %d); %s" % (
item.result(), i,
code.error_goto_if_null(item.result(), self.pos)))
- code.put_gotref(item.result())
+ code.put_gotref(item.result(), item.type)
else:
code.putln("{")
code.putln("Py_ssize_t i;")
@@ -7149,7 +7933,7 @@
code.putln("for (i=0; i < %s; i++) {" % len(self.unpacked_items))
code.putln("PyObject* item = PySequence_ITEM(sequence, i); %s" % (
code.error_goto_if_null('item', self.pos)))
- code.put_gotref('item')
+ code.put_gotref('item', py_object_type)
code.putln("*(temps[i]) = item;")
code.putln("}")
code.putln("}")
@@ -7173,9 +7957,11 @@
code.putln("}")
def generate_generic_parallel_unpacking_code(self, code, rhs, unpacked_items, use_loop, terminate=True):
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
- code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c"))
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
+ code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
if use_loop:
code.putln("PyObject** temps[%s] = {%s};" % (
@@ -7188,11 +7974,11 @@
iterator_temp,
rhs.py_result(),
code.error_goto_if_null(iterator_temp, self.pos)))
- code.put_gotref(iterator_temp)
+ code.put_gotref(iterator_temp, py_object_type)
rhs.generate_disposal_code(code)
iternext_func = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- code.putln("%s = Py_TYPE(%s)->tp_iternext;" % (
+ code.putln("%s = __Pyx_PyObject_GetIterNextFunc(%s);" % (
iternext_func, iterator_temp))
unpacking_error_label = code.new_label('unpacking_failed')
@@ -7201,7 +7987,7 @@
code.putln("for (index=0; index < %s; index++) {" % len(unpacked_items))
code.put("PyObject* item = %s; if (unlikely(!item)) " % unpack_code)
code.put_goto(unpacking_error_label)
- code.put_gotref("item")
+ code.put_gotref("item", py_object_type)
code.putln("*(temps[index]) = item;")
code.putln("}")
else:
@@ -7213,7 +7999,7 @@
unpack_code,
item.result()))
code.put_goto(unpacking_error_label)
- code.put_gotref(item.py_result())
+ item.generate_gotref(code)
if terminate:
code.globalstate.use_utility_code(
@@ -7266,11 +8052,14 @@
starred_target.allocate(code)
target_list = starred_target.result()
- code.putln("%s = PySequence_List(%s); %s" % (
+ code.putln("%s = %s(%s); %s" % (
target_list,
+ "__Pyx_PySequence_ListKeepNew" if (
+ not iterator_temp and rhs.is_temp and rhs.type in (py_object_type, list_type))
+ else "PySequence_List",
iterator_temp or rhs.py_result(),
code.error_goto_if_null(target_list, self.pos)))
- code.put_gotref(target_list)
+ starred_target.generate_gotref(code)
if iterator_temp:
code.put_decref_clear(iterator_temp, py_object_type)
@@ -7279,7 +8068,8 @@
rhs.generate_disposal_code(code)
if unpacked_fixed_items_right:
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c"))
length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list))
code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right)))
@@ -7301,7 +8091,7 @@
code.putln("%s = PySequence_ITEM(%s, %s-%d); " % (
item.py_result(), target_list, length_temp, i+1))
code.putln('#endif')
- code.put_gotref(item.py_result())
+ item.generate_gotref(code)
coerced_arg.generate_evaluation_code(code)
code.putln('#if !CYTHON_COMPILING_IN_CPYTHON')
@@ -7309,12 +8099,12 @@
code.putln('%s = PySequence_GetSlice(%s, 0, %s-%d); %s' % (
sublist_temp, target_list, length_temp, len(unpacked_fixed_items_right),
code.error_goto_if_null(sublist_temp, self.pos)))
- code.put_gotref(sublist_temp)
+ code.put_gotref(sublist_temp, py_object_type)
code.funcstate.release_temp(length_temp)
code.put_decref(target_list, py_object_type)
code.putln('%s = %s; %s = NULL;' % (target_list, sublist_temp, sublist_temp))
code.putln('#else')
- code.putln('%s = %s;' % (sublist_temp, sublist_temp)) # avoid warning about unused variable
+ code.putln('(void)%s;' % sublist_temp) # avoid warning about unused variable
code.funcstate.release_temp(sublist_temp)
code.putln('#endif')
@@ -7343,10 +8133,10 @@
if self.mult_factor or not self.args:
return tuple_type
arg_types = [arg.infer_type(env) for arg in self.args]
- if any(type.is_pyobject or type.is_unspecified or type.is_fused for type in arg_types):
+ if any(type.is_pyobject or type.is_memoryviewslice or type.is_unspecified or type.is_fused
+ for type in arg_types):
return tuple_type
- else:
- return env.declare_tuple_type(self.pos, arg_types).type
+ return env.declare_tuple_type(self.pos, arg_types).type
def analyse_types(self, env, skip_children=False):
if len(self.args) == 0:
@@ -7360,7 +8150,8 @@
arg.starred_expr_allowed_here = True
self.args[i] = arg.analyse_types(env)
if (not self.mult_factor and
- not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_fused) for arg in self.args)):
+ not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_memoryviewslice or arg.type.is_fused)
+ for arg in self.args)):
self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type
self.is_temp = 1
return self
@@ -7443,26 +8234,26 @@
if len(self.args) == 0:
# result_code is Naming.empty_tuple
return
- if self.is_partly_literal:
- # underlying tuple is const, but factor is not
- tuple_target = code.get_py_const(py_object_type, 'tuple', cleanup_level=2)
- const_code = code.get_cached_constants_writer()
- const_code.mark_pos(self.pos)
- self.generate_sequence_packing_code(const_code, tuple_target, plain=True)
- const_code.put_giveref(tuple_target)
- code.putln('%s = PyNumber_Multiply(%s, %s); %s' % (
- self.result(), tuple_target, self.mult_factor.py_result(),
- code.error_goto_if_null(self.result(), self.pos)
+
+ if self.is_literal or self.is_partly_literal:
+ # The "mult_factor" is part of the deduplication if it is also constant, i.e. when
+ # we deduplicate the multiplied result. Otherwise, only deduplicate the constant part.
+ dedup_key = make_dedup_key(self.type, [self.mult_factor if self.is_literal else None] + self.args)
+ tuple_target = code.get_py_const(py_object_type, 'tuple', cleanup_level=2, dedup_key=dedup_key)
+ const_code = code.get_cached_constants_writer(tuple_target)
+ if const_code is not None:
+ # constant is not yet initialised
+ const_code.mark_pos(self.pos)
+ self.generate_sequence_packing_code(const_code, tuple_target, plain=not self.is_literal)
+ const_code.put_giveref(tuple_target, py_object_type)
+ if self.is_literal:
+ self.result_code = tuple_target
+ else:
+ code.putln('%s = PyNumber_Multiply(%s, %s); %s' % (
+ self.result(), tuple_target, self.mult_factor.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)
))
- code.put_gotref(self.py_result())
- elif self.is_literal:
- # non-empty cached tuple => result is global constant,
- # creation code goes into separate code writer
- self.result_code = code.get_py_const(py_object_type, 'tuple', cleanup_level=2)
- code = code.get_cached_constants_writer()
- code.mark_pos(self.pos)
- self.generate_sequence_packing_code(code)
- code.put_giveref(self.py_result())
+ self.generate_gotref(code)
else:
self.type.entry.used = True
self.generate_sequence_packing_code(code)
@@ -7484,7 +8275,7 @@
return ()
def infer_type(self, env):
- # TOOD: Infer non-object list arrays.
+ # TODO: Infer non-object list arrays.
return list_type
def analyse_expressions(self, env):
@@ -7495,11 +8286,10 @@
return node.coerce_to_pyobject(env)
def analyse_types(self, env):
- hold_errors()
- self.original_args = list(self.args)
- node = SequenceNode.analyse_types(self, env)
- node.obj_conversion_errors = held_errors()
- release_errors(ignore=True)
+ with local_errors(ignore=True) as errors:
+ self.original_args = list(self.args)
+ node = SequenceNode.analyse_types(self, env)
+ node.obj_conversion_errors = errors
if env.is_module_scope:
self.in_module_scope = True
node = node._create_merge_node_if_necessary(env)
@@ -7562,20 +8352,18 @@
return t
def allocate_temp_result(self, code):
- if self.type.is_array and self.in_module_scope:
- self.temp_code = code.funcstate.allocate_temp(
- self.type, manage_ref=False, static=True)
- else:
- SequenceNode.allocate_temp_result(self, code)
-
- def release_temp_result(self, env):
if self.type.is_array:
- # To be valid C++, we must allocate the memory on the stack
- # manually and be sure not to reuse it for something else.
- # Yes, this means that we leak a temp array variable.
- pass
+ if self.in_module_scope:
+ self.temp_code = code.funcstate.allocate_temp(
+ self.type, manage_ref=False, static=True, reusable=False)
+ else:
+ # To be valid C++, we must allocate the memory on the stack
+ # manually and be sure not to reuse it for something else.
+ # Yes, this means that we leak a temp array variable.
+ self.temp_code = code.funcstate.allocate_temp(
+ self.type, manage_ref=False, reusable=False)
else:
- SequenceNode.release_temp_result(self, env)
+ SequenceNode.allocate_temp_result(self, code)
def calculate_constant_result(self):
if self.mult_factor:
@@ -7648,7 +8436,7 @@
if expr_scope is not None:
self.expr_scope = expr_scope
elif self.has_local_scope:
- self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
+ self.expr_scope = Symtab.ComprehensionScope(outer_scope)
else:
self.expr_scope = None
@@ -7677,9 +8465,8 @@
code.putln('{ /* enter inner scope */')
py_entries = []
- for entry in self.expr_scope.var_entries:
+ for _, entry in sorted(item for item in self.expr_scope.entries.items() if item[0]):
if not entry.in_closure:
- code.put_var_declaration(entry)
if entry.type.is_pyobject and entry.used:
py_entries.append(entry)
if not py_entries:
@@ -7689,14 +8476,13 @@
return
# must free all local Python references at each exit point
- old_loop_labels = tuple(code.new_loop_labels())
+ old_loop_labels = code.new_loop_labels()
old_error_label = code.new_error_label()
generate_inner_evaluation_code(code)
# normal (non-error) exit
- for entry in py_entries:
- code.put_var_decref(entry)
+ self._generate_vars_cleanup(code, py_entries)
# error/loop body exit points
exit_scope = code.new_label('exit_scope')
@@ -7705,8 +8491,7 @@
list(zip(code.get_loop_labels(), old_loop_labels))):
if code.label_used(label):
code.put_label(label)
- for entry in py_entries:
- code.put_var_decref(entry)
+ self._generate_vars_cleanup(code, py_entries)
code.put_goto(old_label)
code.put_label(exit_scope)
code.putln('} /* exit inner scope */')
@@ -7714,6 +8499,14 @@
code.set_loop_labels(old_loop_labels)
code.error_label = old_error_label
+ def _generate_vars_cleanup(self, code, py_entries):
+ for entry in py_entries:
+ if entry.is_cglobal:
+ code.put_var_gotref(entry)
+ code.put_var_decref_set(entry, "Py_None")
+ else:
+ code.put_var_xdecref_clear(entry)
+
class ComprehensionNode(ScopedExprNode):
# A list/set/dict comprehension
@@ -7721,12 +8514,13 @@
child_attrs = ["loop"]
is_temp = True
+ constant_result = not_a_constant
def infer_type(self, env):
return self.type
def analyse_declarations(self, env):
- self.append.target = self # this is used in the PyList_Append of the inner loop
+ self.append.target = self # this is used in the PyList_Append of the inner loop
self.init_scope(env)
def analyse_scoped_declarations(self, env):
@@ -7761,7 +8555,7 @@
self.result(), create_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
self.loop.generate_execution_code(code)
def annotate(self, code):
@@ -7886,7 +8680,7 @@
code.putln("%s = __Pyx_Generator_Next(%s); %s" % (
self.result(), self.gen.result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
class MergedSequenceNode(ExprNode):
@@ -7994,10 +8788,12 @@
else:
code.putln("%s = %s(%s); %s" % (
self.result(),
- 'PySet_New' if is_set else 'PySequence_List',
+ 'PySet_New' if is_set
+ else "__Pyx_PySequence_ListKeepNew" if item.is_temp and item.type in (py_object_type, list_type)
+ else "PySequence_List",
item.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
item.generate_disposal_code(code)
item.free_temps(code)
@@ -8047,7 +8843,7 @@
self.result(),
Naming.quick_temp_cname,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
code.putln("}")
for helper in sorted(helpers):
@@ -8080,7 +8876,7 @@
return False
def calculate_constant_result(self):
- self.constant_result = set([arg.constant_result for arg in self.args])
+ self.constant_result = {arg.constant_result for arg in self.args}
def compile_time_value(self, denv):
values = [arg.compile_time_value(denv) for arg in self.args]
@@ -8097,7 +8893,7 @@
"%s = PySet_New(0); %s" % (
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
for arg in self.args:
code.put_error_if_neg(
self.pos,
@@ -8144,15 +8940,16 @@
return ()
def infer_type(self, env):
- # TOOD: Infer struct constructors.
+ # TODO: Infer struct constructors.
return dict_type
def analyse_types(self, env):
- hold_errors()
- self.key_value_pairs = [ item.analyse_types(env)
- for item in self.key_value_pairs ]
- self.obj_conversion_errors = held_errors()
- release_errors(ignore=True)
+ with local_errors(ignore=True) as errors:
+ self.key_value_pairs = [
+ item.analyse_types(env)
+ for item in self.key_value_pairs
+ ]
+ self.obj_conversion_errors = errors
return self
def may_be_none(self):
@@ -8183,7 +8980,7 @@
error(item.key.pos, "Invalid struct field identifier")
item.key = StringNode(item.key.pos, value="")
else:
- key = str(item.key.value) # converts string literals to unicode in Py3
+ key = str(item.key.value) # converts string literals to unicode in Py3
member = dst_type.scope.lookup_here(key)
if not member:
error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key))
@@ -8193,8 +8990,7 @@
value = value.arg
item.value = value.coerce_to(member.type, env)
else:
- self.type = error_type
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
+ return super(DictNode, self).coerce_to(dst_type, env)
return self
def release_errors(self):
@@ -8214,10 +9010,11 @@
if is_dict:
self.release_errors()
code.putln(
- "%s = PyDict_New(); %s" % (
+ "%s = __Pyx_PyDict_NewPresized(%d); %s" % (
self.result(),
+ len(self.key_value_pairs),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
keys_seen = set()
key_type = None
@@ -8281,6 +9078,11 @@
for item in self.key_value_pairs:
item.annotate(code)
+ def as_python_dict(self):
+ # returns a dict with constant keys and Node values
+ # (only works on DictNodes where the keys are ConstNodes or PyConstNode)
+ return dict([(key.value, value) for key, value in self.key_value_pairs])
+
class DictItemNode(ExprNode):
# Represents a single item in a DictNode
@@ -8289,7 +9091,7 @@
# value ExprNode
subexprs = ['key', 'value']
- nogil_check = None # Parent DictNode takes care of it
+ nogil_check = None # Parent DictNode takes care of it
def calculate_constant_result(self):
self.constant_result = (
@@ -8345,7 +9147,7 @@
code.putln('%s = PyDict_Keys(%s); %s' % (
self.result(), dict_result,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
else:
# originally used PyMapping_Keys() here, but that may return a tuple
code.globalstate.use_utility_code(UtilityCode.load_cached(
@@ -8354,11 +9156,11 @@
code.putln('%s = __Pyx_PyObject_CallMethod0(%s, %s); %s' % (
self.result(), dict_result, keys_cname,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
code.putln("if (unlikely(!PyList_Check(%s))) {" % self.result())
- code.put_decref_set(self.result(), "PySequence_List(%s)" % self.result())
+ self.generate_decref_set(code, "PySequence_List(%s)" % self.result())
code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
code.putln("}")
code.put_error_if_neg(
self.pos, 'PyList_Sort(%s)' % self.py_result())
@@ -8380,21 +9182,22 @@
# a name, tuple of bases and class dictionary.
#
# name EncodedString Name of the class
- # bases ExprNode Base class tuple
- # dict ExprNode Class dict (not owned by this node)
+ # class_def_node PyClassDefNode PyClassDefNode defining this class
# doc ExprNode or None Doc string
# module_name EncodedString Name of defining module
- subexprs = ['bases', 'doc']
+ subexprs = ['doc']
type = py_object_type
is_temp = True
+ def analyse_annotations(self, env):
+ pass
+
def infer_type(self, env):
# TODO: could return 'type' in some cases
return py_object_type
def analyse_types(self, env):
- self.bases = self.bases.analyse_types(env)
if self.doc:
self.doc = self.doc.analyse_types(env)
self.doc = self.doc.coerce_to_pyobject(env)
@@ -8407,12 +9210,13 @@
gil_message = "Constructing Python class"
def generate_result_code(self, code):
+ class_def_node = self.class_def_node
cname = code.intern_identifier(self.name)
if self.doc:
code.put_error_if_neg(self.pos,
'PyDict_SetItem(%s, %s, %s)' % (
- self.dict.py_result(),
+ class_def_node.dict.py_result(),
code.intern_identifier(
StringEncoding.EncodedString("__doc__")),
self.doc.py_result()))
@@ -8421,13 +9225,13 @@
code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
self.result(),
- self.bases.py_result(),
- self.dict.py_result(),
+ class_def_node.bases.py_result(),
+ class_def_node.dict.py_result(),
cname,
qualname,
py_mod_name,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class Py3ClassNode(ExprNode):
@@ -8436,13 +9240,15 @@
# a name, tuple of bases and class dictionary.
#
# name EncodedString Name of the class
- # dict ExprNode Class dict (not owned by this node)
# module_name EncodedString Name of defining module
+ # class_def_node PyClassDefNode PyClassDefNode defining this class
# calculate_metaclass bool should call CalculateMetaclass()
# allow_py2_metaclass bool should look for Py2 metaclass
+ # force_type bool always create a "new style" class, even with no bases
subexprs = []
type = py_object_type
+ force_type = False
is_temp = True
def infer_type(self, env):
@@ -8457,15 +9263,35 @@
gil_message = "Constructing Python class"
+ def analyse_annotations(self, env):
+ from .AutoDocTransforms import AnnotationWriter
+ position = self.class_def_node.pos
+ dict_items = [
+ DictItemNode(
+ entry.pos,
+ key=IdentifierStringNode(entry.pos, value=entry.name),
+ value=entry.annotation.string
+ )
+ for entry in env.entries.values() if entry.annotation
+ ]
+ # Annotations dict shouldn't exist for classes which don't declare any.
+ if dict_items:
+ annotations_dict = DictNode(position, key_value_pairs=dict_items)
+ lhs = NameNode(position, name=StringEncoding.EncodedString(u"__annotations__"))
+ lhs.entry = env.lookup_here(lhs.name) or env.declare_var(lhs.name, dict_type, position)
+ node = SingleAssignmentNode(position, lhs=lhs, rhs=annotations_dict)
+ node.analyse_declarations(env)
+ self.class_def_node.body.stats.insert(0, node)
+
def generate_result_code(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
cname = code.intern_identifier(self.name)
- if self.mkw:
- mkw = self.mkw.py_result()
- else:
- mkw = 'NULL'
- if self.metaclass:
- metaclass = self.metaclass.result()
+ class_def_node = self.class_def_node
+ mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL'
+ if class_def_node.metaclass:
+ metaclass = class_def_node.metaclass.py_result()
+ elif self.force_type:
+ metaclass = "((PyObject*)&PyType_Type)"
else:
metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
code.putln(
@@ -8473,20 +9299,19 @@
self.result(),
metaclass,
cname,
- self.bases.py_result(),
- self.dict.py_result(),
+ class_def_node.bases.py_result(),
+ class_def_node.dict.py_result(),
mkw,
self.calculate_metaclass,
self.allow_py2_metaclass,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class PyClassMetaclassNode(ExprNode):
# Helper class holds Python3 metaclass object
#
- # bases ExprNode Base class tuple (not owned by this node)
- # mkw ExprNode Class keyword arguments (not owned by this node)
+ # class_def_node PyClassDefNode PyClassDefNode defining this class
subexprs = []
@@ -8499,38 +9324,38 @@
return True
def generate_result_code(self, code):
- if self.mkw:
+ bases = self.class_def_node.bases
+ mkw = self.class_def_node.mkw
+ if mkw:
code.globalstate.use_utility_code(
UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
- self.bases.result(),
- self.mkw.result())
+ bases.result(),
+ mkw.result())
else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
- self.bases.result())
+ bases.result())
code.putln(
"%s = %s; %s" % (
self.result(), call,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
+
class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
# Helper class holds Python3 namespace object
#
# All this are not owned by this node
- # metaclass ExprNode Metaclass object
- # bases ExprNode Base class tuple
- # mkw ExprNode Class keyword arguments
+ # class_def_node PyClassDefNode PyClassDefNode defining this class
# doc ExprNode or None Doc string (owned)
subexprs = ['doc']
def analyse_types(self, env):
if self.doc:
- self.doc = self.doc.analyse_types(env)
- self.doc = self.doc.coerce_to_pyobject(env)
+ self.doc = self.doc.analyse_types(env).coerce_to_pyobject(env)
self.type = py_object_type
self.is_temp = 1
return self
@@ -8542,30 +9367,23 @@
cname = code.intern_identifier(self.name)
py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code)
- if self.doc:
- doc_code = self.doc.result()
- else:
- doc_code = '(PyObject *) NULL'
- if self.mkw:
- mkw = self.mkw.py_result()
- else:
- mkw = '(PyObject *) NULL'
- if self.metaclass:
- metaclass = self.metaclass.result()
- else:
- metaclass = "(PyObject *) NULL"
+ class_def_node = self.class_def_node
+ null = "(PyObject *) NULL"
+ doc_code = self.doc.result() if self.doc else null
+ mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null
+ metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null
code.putln(
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
self.result(),
metaclass,
- self.bases.result(),
+ class_def_node.bases.result(),
cname,
qualname,
mkw,
py_mod_name,
doc_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class ClassCellInjectorNode(ExprNode):
@@ -8576,110 +9394,48 @@
is_active = False
def analyse_expressions(self, env):
- if self.is_active:
- env.use_utility_code(
- UtilityCode.load_cached("CyFunctionClassCell", "CythonFunction.c"))
return self
- def generate_evaluation_code(self, code):
- if self.is_active:
- self.allocate_temp_result(code)
- code.putln(
- '%s = PyList_New(0); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
-
- def generate_injection_code(self, code, classobj_cname):
- if self.is_active:
- code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % (
- self.result(), classobj_cname))
-
-
-class ClassCellNode(ExprNode):
- # Class Cell for noargs super()
- subexprs = []
- is_temp = True
- is_generator = False
- type = py_object_type
-
- def analyse_types(self, env):
- return self
-
- def generate_result_code(self, code):
- if not self.is_generator:
- code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
- self.result(),
- Naming.self_cname))
- else:
- code.putln('%s = %s->classobj;' % (
- self.result(), Naming.generator_cname))
- code.putln(
- 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
- '"super(): empty __class__ cell"); %s }' % (
- self.result(),
- code.error_goto(self.pos)))
- code.put_incref(self.result(), py_object_type)
-
-
-class BoundMethodNode(ExprNode):
- # Helper class used in the implementation of Python
- # class definitions. Constructs an bound method
- # object from a class and a function.
- #
- # function ExprNode Function object
- # self_object ExprNode self object
-
- subexprs = ['function']
-
- def analyse_types(self, env):
- self.function = self.function.analyse_types(env)
- self.type = py_object_type
- self.is_temp = 1
- return self
-
- gil_message = "Constructing a bound method"
-
def generate_result_code(self, code):
+ assert self.is_active
code.putln(
- "%s = __Pyx_PyMethod_New(%s, %s, (PyObject*)%s->ob_type); %s" % (
+ '%s = PyList_New(0); %s' % (
self.result(),
- self.function.py_result(),
- self.self_object.py_result(),
- self.self_object.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
+
+ def generate_injection_code(self, code, classobj_cname):
+ assert self.is_active
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("CyFunctionClassCell", "CythonFunction.c"))
+ code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % (
+ self.result(), classobj_cname))
-class UnboundMethodNode(ExprNode):
- # Helper class used in the implementation of Python
- # class definitions. Constructs an unbound method
- # object from a class and a function.
- #
- # function ExprNode Function object
+class ClassCellNode(ExprNode):
+ # Class Cell for noargs super()
+ subexprs = []
+ is_temp = True
+ is_generator = False
type = py_object_type
- is_temp = 1
-
- subexprs = ['function']
def analyse_types(self, env):
- self.function = self.function.analyse_types(env)
return self
- def may_be_none(self):
- return False
-
- gil_message = "Constructing an unbound method"
-
def generate_result_code(self, code):
- class_cname = code.pyclass_stack[-1].classobj.result()
+ if not self.is_generator:
+ code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
+ self.result(),
+ Naming.self_cname))
+ else:
+ code.putln('%s = %s->classobj;' % (
+ self.result(), Naming.generator_cname))
code.putln(
- "%s = __Pyx_PyMethod_New(%s, 0, %s); %s" % (
+ 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
+ '"super(): empty __class__ cell"); %s }' % (
self.result(),
- self.function.py_result(),
- class_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ code.error_goto(self.pos)))
+ code.put_incref(self.result(), py_object_type)
class PyCFunctionNode(ExprNode, ModuleNameMixin):
@@ -8688,7 +9444,6 @@
# from a PyMethodDef struct.
#
# pymethdef_cname string PyMethodDef structure
- # self_object ExprNode or None
# binding bool
# def_node DefNode the Python function node
# module_name EncodedString Name of defining module
@@ -8697,7 +9452,6 @@
subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict',
'annotations_dict']
- self_object = None
code_object = None
binding = False
def_node = None
@@ -8746,36 +9500,35 @@
must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
for arg in self.def_node.args:
- if arg.default and not must_use_constants:
- if not arg.default.is_literal:
- arg.is_dynamic = True
- if arg.type.is_pyobject:
- nonliteral_objects.append(arg)
+ if arg.default:
+ if not must_use_constants:
+ if arg.default.is_literal:
+ arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
else:
- nonliteral_other.append(arg)
- else:
- arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
- if arg.kw_only:
- default_kwargs.append(arg)
- else:
- default_args.append(arg)
+ arg.is_dynamic = True
+ if arg.type.is_pyobject:
+ nonliteral_objects.append(arg)
+ else:
+ nonliteral_other.append(arg)
+ if arg.default.type and arg.default.type.can_coerce_to_pyobject(env):
+ if arg.kw_only:
+ default_kwargs.append(arg)
+ else:
+ default_args.append(arg)
if arg.annotation:
arg.annotation = arg.annotation.analyse_types(env)
- if not arg.annotation.type.is_pyobject:
- arg.annotation = arg.annotation.coerce_to_pyobject(env)
- annotations.append((arg.pos, arg.name, arg.annotation))
+ annotations.append((arg.pos, arg.name, arg.annotation.string))
for arg in (self.def_node.star_arg, self.def_node.starstar_arg):
if arg and arg.annotation:
arg.annotation = arg.annotation.analyse_types(env)
- if not arg.annotation.type.is_pyobject:
- arg.annotation = arg.annotation.coerce_to_pyobject(env)
- annotations.append((arg.pos, arg.name, arg.annotation))
-
- if self.def_node.return_type_annotation:
- annotations.append((self.def_node.return_type_annotation.pos,
- StringEncoding.EncodedString("return"),
- self.def_node.return_type_annotation))
+ annotations.append((arg.pos, arg.name, arg.annotation.string))
+
+ annotation = self.def_node.return_type_annotation
+ if annotation:
+ self.def_node.return_type_annotation = annotation.analyse_types(env)
+ annotations.append((annotation.pos, StringEncoding.EncodedString("return"),
+ annotation.string))
if nonliteral_objects or nonliteral_other:
module_scope = env.global_scope()
@@ -8783,14 +9536,17 @@
scope = Symtab.StructOrUnionScope(cname)
self.defaults = []
for arg in nonliteral_objects:
- entry = scope.declare_var(arg.name, arg.type, None,
+ type_ = arg.type
+ if type_.is_buffer:
+ type_ = type_.base
+ entry = scope.declare_var(arg.name, type_, None,
Naming.arg_prefix + arg.name,
allow_pyobject=True)
self.defaults.append((arg, entry))
for arg in nonliteral_other:
entry = scope.declare_var(arg.name, arg.type, None,
Naming.arg_prefix + arg.name,
- allow_pyobject=False)
+ allow_pyobject=False, allow_memoryview=True)
self.defaults.append((arg, entry))
entry = module_scope.declare_struct_or_union(
None, 'struct', scope, 1, None, cname=cname)
@@ -8815,7 +9571,8 @@
value=arg.default)
for arg in default_kwargs])
self.defaults_kwdict = defaults_kwdict.analyse_types(env)
- else:
+ elif not self.specialized_cpdefs:
+ # Fused dispatch functions do not support (dynamic) default arguments, only the specialisations do.
if default_args:
defaults_tuple = DefaultsTupleNode(
self.pos, default_args, self.defaults_struct)
@@ -8857,12 +9614,8 @@
gil_message = "Constructing Python function"
- def self_result_code(self):
- if self.self_object is None:
- self_result = "NULL"
- else:
- self_result = self.self_object.py_result()
- return self_result
+ def closure_result_code(self):
+ return "NULL"
def generate_result_code(self, code):
if self.binding:
@@ -8876,11 +9629,11 @@
'%s = PyCFunction_NewEx(&%s, %s, %s); %s' % (
self.result(),
self.pymethdef_cname,
- self.self_result_code(),
+ self.closure_result_code(),
py_mod_name,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
def generate_cyfunction_code(self, code):
if self.specialized_cpdefs:
@@ -8891,11 +9644,11 @@
if self.specialized_cpdefs or self.is_specialization:
code.globalstate.use_utility_code(
UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
- constructor = "__pyx_FusedFunction_NewEx"
+ constructor = "__pyx_FusedFunction_New"
else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
- constructor = "__Pyx_CyFunction_NewEx"
+ constructor = "__Pyx_CyFunction_New"
if self.code_object:
code_object_result = self.code_object.py_result()
@@ -8911,6 +9664,9 @@
if def_node.local_scope.parent_scope.is_c_class_scope and not def_node.entry.is_anonymous:
flags.append('__Pyx_CYFUNCTION_CCLASS')
+ if def_node.is_coroutine:
+ flags.append('__Pyx_CYFUNCTION_COROUTINE')
+
if flags:
flags = ' | '.join(flags)
else:
@@ -8923,13 +9679,13 @@
self.pymethdef_cname,
flags,
self.get_py_qualified_name(code),
- self.self_result_code(),
+ self.closure_result_code(),
self.get_py_mod_name(code),
Naming.moddict_cname,
code_object_result,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
if def_node.requires_classobj:
assert code.pyclass_stack, "pyclass_stack is empty"
@@ -8939,7 +9695,7 @@
'PyList_Append(%s, %s);' % (
class_node.class_cell.result(),
self.result()))
- code.put_giveref(self.py_result())
+ self.generate_giveref(code)
if self.defaults:
code.putln(
@@ -8955,26 +9711,29 @@
if self.defaults_tuple:
code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
self.result(), self.defaults_tuple.py_result()))
- if self.defaults_kwdict:
- code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
- self.result(), self.defaults_kwdict.py_result()))
- if def_node.defaults_getter:
- code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
- self.result(), def_node.defaults_getter.entry.pyfunc_cname))
- if self.annotations_dict:
- code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
- self.result(), self.annotations_dict.py_result()))
+ if not self.specialized_cpdefs:
+ # disable introspection functions for fused dispatcher function since the user never sees it
+ # TODO: this is mostly disabled because the attributes end up pointing to ones belonging
+ # to the specializations - ideally this would be fixed instead
+ if self.defaults_kwdict:
+ code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
+ self.result(), self.defaults_kwdict.py_result()))
+ if def_node.defaults_getter:
+ code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
+ self.result(), def_node.defaults_getter.entry.pyfunc_cname))
+ if self.annotations_dict:
+ code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
+ self.result(), self.annotations_dict.py_result()))
class InnerFunctionNode(PyCFunctionNode):
# Special PyCFunctionNode that depends on a closure class
- #
binding = True
- needs_self_code = True
+ needs_closure_code = True
- def self_result_code(self):
- if self.needs_self_code:
+ def closure_result_code(self):
+ if self.needs_closure_code:
return "((PyObject*)%s)" % Naming.cur_scope_cname
return "NULL"
@@ -9013,7 +9772,9 @@
if self.result_code is None:
self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- code = code.get_cached_constants_writer()
+ code = code.get_cached_constants_writer(self.result_code)
+ if code is None:
+ return # already initialised
code.mark_pos(self.pos)
func = self.def_node
func_name = code.get_py_string_const(
@@ -9022,15 +9783,18 @@
file_path = StringEncoding.bytes_literal(func.pos[0].get_filenametable_entry().encode('utf8'), 'utf8')
file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
- flags = []
+ # This combination makes CPython create a new dict for "frame.f_locals" (see GH #1836).
+ flags = ['CO_OPTIMIZED', 'CO_NEWLOCALS']
+
if self.def_node.star_arg:
flags.append('CO_VARARGS')
if self.def_node.starstar_arg:
flags.append('CO_VARKEYWORDS')
- code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
+ code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
self.result_code,
len(func.args) - func.num_kwonly_args, # argcount
+ func.num_posonly_args, # posonlyargcount (Py3.8+ only)
func.num_kwonly_args, # kwonlyargcount (Py3 only)
len(self.varnames.args), # nlocals
'|'.join(flags) or '0', # flags
@@ -9149,11 +9913,14 @@
name = StringEncoding.EncodedString('')
def analyse_declarations(self, env):
+ if hasattr(self, "lambda_name"):
+ # this if-statement makes it safe to run twice
+ return
self.lambda_name = self.def_node.lambda_name = env.next_id('lambda')
self.def_node.no_assignment_synthesis = True
self.def_node.pymethdef_required = True
- self.def_node.analyse_declarations(env)
self.def_node.is_cyfunction = True
+ self.def_node.analyse_declarations(env)
self.pymethdef_cname = self.def_node.entry.pymethdef_cname
env.add_lambda_def(self.def_node)
@@ -9178,6 +9945,9 @@
binding = False
def analyse_declarations(self, env):
+ if hasattr(self, "genexpr_name"):
+ # this if-statement makes it safe to run twice
+ return
self.genexpr_name = env.next_id('genexpr')
super(GeneratorExpressionNode, self).analyse_declarations(env)
# No pymethdef required
@@ -9192,9 +9962,9 @@
'%s = %s(%s); %s' % (
self.result(),
self.def_node.entry.pyfunc_cname,
- self.self_result_code(),
+ self.closure_result_code(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class YieldExprNode(ExprNode):
@@ -9209,10 +9979,11 @@
label_num = 0
is_yield_from = False
is_await = False
+ in_async_gen = False
expr_keyword = 'yield'
def analyse_types(self, env):
- if not self.label_num:
+ if not self.label_num or (self.is_yield_from and self.in_async_gen):
error(self.pos, "'%s' not supported here" % self.expr_keyword)
self.is_temp = 1
if self.arg is not None:
@@ -9243,7 +10014,8 @@
Generate the code to return the argument in 'Naming.retval_cname'
and to continue at the yield label.
"""
- label_num, label_name = code.new_yield_label()
+ label_num, label_name = code.new_yield_label(
+ self.expr_keyword.replace(' ', '_'))
code.use_label(label_name)
saved = []
@@ -9251,44 +10023,62 @@
for cname, type, manage_ref in code.funcstate.temps_in_use():
save_cname = code.funcstate.closure_temps.allocate_temp(type)
saved.append((cname, save_cname, type))
- if type.is_pyobject:
- code.put_xgiveref(cname)
+ if type.is_cpp_class:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("MoveIfSupported", "CppSupport.cpp"))
+ cname = "__PYX_STD_MOVE_IF_SUPPORTED(%s)" % cname
+ else:
+ code.put_xgiveref(cname, type)
code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
- code.put_xgiveref(Naming.retval_cname)
+ code.put_xgiveref(Naming.retval_cname, py_object_type)
+ profile = code.globalstate.directives['profile']
+ linetrace = code.globalstate.directives['linetrace']
+ if profile or linetrace:
+ code.put_trace_return(Naming.retval_cname,
+ nogil=not code.funcstate.gil_owned)
code.put_finish_refcount_context()
- code.putln("/* return from generator, yielding value */")
+
+ if code.funcstate.current_except is not None:
+ # inside of an except block => save away currently handled exception
+ code.putln("__Pyx_Coroutine_SwapException(%s);" % Naming.generator_cname)
+ else:
+ # no exceptions being handled => restore exception state of caller
+ code.putln("__Pyx_Coroutine_ResetAndClearException(%s);" % Naming.generator_cname)
+
+ code.putln("/* return from %sgenerator, %sing value */" % (
+ 'async ' if self.in_async_gen else '',
+ 'await' if self.is_await else 'yield'))
code.putln("%s->resume_label = %d;" % (
Naming.generator_cname, label_num))
- code.putln("return %s;" % Naming.retval_cname)
+ if self.in_async_gen and not self.is_await:
+ # __Pyx__PyAsyncGenValueWrapperNew() steals a reference to the return value
+ code.putln("return __Pyx__PyAsyncGenValueWrapperNew(%s);" % Naming.retval_cname)
+ else:
+ code.putln("return %s;" % Naming.retval_cname)
code.put_label(label_name)
for cname, save_cname, type in saved:
- code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
+ save_cname = "%s->%s" % (Naming.cur_scope_cname, save_cname)
+ if type.is_cpp_class:
+ save_cname = "__PYX_STD_MOVE_IF_SUPPORTED(%s)" % save_cname
+ code.putln('%s = %s;' % (cname, save_cname))
if type.is_pyobject:
- code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
- code.put_xgotref(cname)
- code.putln(code.error_goto_if_null(Naming.sent_value_cname, self.pos))
+ code.putln('%s = 0;' % save_cname)
+ code.put_xgotref(cname, type)
+ self.generate_sent_value_handling_code(code, Naming.sent_value_cname)
if self.result_is_used:
self.allocate_temp_result(code)
code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname))
code.put_incref(self.result(), py_object_type)
+ def generate_sent_value_handling_code(self, code, value_cname):
+ code.putln(code.error_goto_if_null(value_cname, self.pos))
-class YieldFromExprNode(YieldExprNode):
- # "yield from GEN" expression
- is_yield_from = True
- expr_keyword = 'yield from'
-
- def coerce_yield_argument(self, env):
- if not self.arg.type.is_string:
- # FIXME: support C arrays and C++ iterators?
- error(self.pos, "yielding from non-Python object not supported")
- self.arg = self.arg.coerce_to_pyobject(env)
+class _YieldDelegationExprNode(YieldExprNode):
def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("GeneratorYieldFrom", "Coroutine.c"))
- return "__Pyx_Generator_Yield_From"
+ raise NotImplementedError()
def generate_evaluation_code(self, code, source_cname=None, decref_source=False):
if source_cname is None:
@@ -9303,7 +10093,7 @@
self.arg.free_temps(code)
elif decref_source:
code.put_decref_clear(source_cname, py_object_type)
- code.put_xgotref(Naming.retval_cname)
+ code.put_xgotref(Naming.retval_cname, py_object_type)
code.putln("if (likely(%s)) {" % Naming.retval_cname)
self.generate_yield_code(code)
@@ -9319,18 +10109,34 @@
# YieldExprNode has allocated the result temp for us
code.putln("%s = NULL;" % self.result())
code.put_error_if_neg(self.pos, "__Pyx_PyGen_FetchStopIterationValue(&%s)" % self.result())
- code.put_gotref(self.result())
+ self.generate_gotref(code)
def handle_iteration_exception(self, code):
- code.putln("PyObject* exc_type = PyErr_Occurred();")
+ code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
code.putln("if (exc_type) {")
- code.putln("if (likely(exc_type == PyExc_StopIteration ||"
- " PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
+ code.putln("if (likely(exc_type == PyExc_StopIteration || (exc_type != PyExc_GeneratorExit &&"
+ " __Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))) PyErr_Clear();")
code.putln("else %s" % code.error_goto(self.pos))
code.putln("}")
-class AwaitExprNode(YieldFromExprNode):
+class YieldFromExprNode(_YieldDelegationExprNode):
+ # "yield from GEN" expression
+ is_yield_from = True
+ expr_keyword = 'yield from'
+
+ def coerce_yield_argument(self, env):
+ if not self.arg.type.is_string:
+ # FIXME: support C arrays and C++ iterators?
+ error(self.pos, "yielding from non-Python object not supported")
+ self.arg = self.arg.coerce_to_pyobject(env)
+
+ def yield_from_func(self, code):
+ code.globalstate.use_utility_code(UtilityCode.load_cached("GeneratorYieldFrom", "Coroutine.c"))
+ return "__Pyx_Generator_Yield_From"
+
+
+class AwaitExprNode(_YieldDelegationExprNode):
# 'await' expression node
#
# arg ExprNode the Awaitable value to await
@@ -9349,29 +10155,34 @@
return "__Pyx_Coroutine_Yield_From"
-class AIterAwaitExprNode(AwaitExprNode):
- # 'await' expression node used in async-for loops to support the pre-Py3.5.2 'aiter' protocol
- def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("CoroutineAIterYieldFrom", "Coroutine.c"))
- return "__Pyx_Coroutine_AIter_Yield_From"
-
-
class AwaitIterNextExprNode(AwaitExprNode):
# 'await' expression node as part of 'async for' iteration
#
# Breaks out of loop on StopAsyncIteration exception.
- def fetch_iteration_result(self, code):
- assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
+ def _generate_break(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c"))
- code.putln("PyObject* exc_type = PyErr_Occurred();")
- code.putln("if (exc_type && likely(exc_type == __Pyx_PyExc_StopAsyncIteration ||"
- " PyErr_GivenExceptionMatches(exc_type, __Pyx_PyExc_StopAsyncIteration))) {")
+ code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
+ code.putln("if (unlikely(exc_type && (exc_type == __Pyx_PyExc_StopAsyncIteration || ("
+ " exc_type != PyExc_StopIteration && exc_type != PyExc_GeneratorExit &&"
+ " __Pyx_PyErr_GivenExceptionMatches(exc_type, __Pyx_PyExc_StopAsyncIteration))))) {")
code.putln("PyErr_Clear();")
code.putln("break;")
code.putln("}")
+
+ def fetch_iteration_result(self, code):
+ assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
+ self._generate_break(code)
super(AwaitIterNextExprNode, self).fetch_iteration_result(code)
+ def generate_sent_value_handling_code(self, code, value_cname):
+ assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
+ code.putln("if (unlikely(!%s)) {" % value_cname)
+ self._generate_break(code)
+ # all non-break exceptions are errors, as in parent class
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+
class GlobalsExprNode(AtomicExprNode):
type = dict_type
@@ -9390,7 +10201,7 @@
code.putln('%s = __Pyx_Globals(); %s' % (
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
class LocalsDictItemNode(DictItemNode):
@@ -9517,7 +10328,10 @@
def analyse_types(self, env):
self.operand = self.operand.analyse_types(env)
- if self.is_py_operation():
+ if self.is_pythran_operation(env):
+ self.type = PythranExpr(pythran_unaryop_type(self.operator, self.operand.type))
+ self.is_temp = 1
+ elif self.is_py_operation():
self.coerce_operand_to_pyobject(env)
self.type = py_object_type
self.is_temp = 1
@@ -9533,6 +10347,11 @@
def is_py_operation(self):
return self.operand.type.is_pyobject or self.operand.type.is_ctuple
+ def is_pythran_operation(self, env):
+ np_pythran = has_np_pythran(env)
+ op_type = self.operand.type
+ return np_pythran and (op_type.is_buffer or op_type.is_pythran_expr)
+
def nogil_check(self, env):
if self.is_py_operation():
self.gil_error()
@@ -9545,12 +10364,21 @@
self.operand = self.operand.coerce_to_pyobject(env)
def generate_result_code(self, code):
- if self.operand.type.is_pyobject:
+ if self.type.is_pythran_expr:
+ code.putln("// Pythran unaryop")
+ code.putln("__Pyx_call_destructor(%s);" % self.result())
+ code.putln("new (&%s) decltype(%s){%s%s};" % (
+ self.result(),
+ self.result(),
+ self.operator,
+ self.operand.pythran_result()))
+ elif self.operand.type.is_pyobject:
self.generate_py_operation_code(code)
elif self.is_temp:
if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
@@ -9563,7 +10391,7 @@
function,
self.operand.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
def type_error(self):
if not self.operand.type.is_error:
@@ -9581,7 +10409,7 @@
self.exception_value = entry.type.exception_value
if self.exception_check == '+':
self.is_temp = True
- if self.exception_value is None:
+ if needs_cpp_exception_conversion(self):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
else:
self.exception_check = ''
@@ -9715,7 +10543,10 @@
def analyse_c_operation(self, env):
if self.operand.type.is_ptr:
- self.type = self.operand.type.base_type
+ if env.is_cpp:
+ self.type = PyrexTypes.CReferenceType(self.operand.type.base_type)
+ else:
+ self.type = self.operand.type.base_type
else:
self.type_error()
@@ -9766,7 +10597,10 @@
self.error("Taking address of non-lvalue (type %s)" % argtype)
return self
if argtype.is_pyobject:
- self.error("Cannot take address of Python variable")
+ self.error("Cannot take address of Python %s" % (
+ "variable '%s'" % self.operand.name if self.operand.is_name else
+ "object attribute '%s'" % self.operand.attribute if self.operand.is_attribute else
+ "object"))
return self
if not argtype.is_cpp_class or not self.type:
self.type = PyrexTypes.c_ptr_type(argtype)
@@ -9787,6 +10621,7 @@
if (self.operand.type.is_cpp_class and self.exception_check == '+'):
translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
@@ -9864,7 +10699,8 @@
error(self.pos, "Python objects cannot be cast from pointers of primitive types")
else:
# Should this be an error?
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.operand.type, self.type))
+ warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
+ self.operand.type, self.type))
self.operand = self.operand.coerce_to_simple(env)
elif from_py and not to_py:
if self.type.create_from_py_utility_code(env):
@@ -9873,7 +10709,8 @@
if not (self.type.base_type.is_void or self.type.base_type.is_struct):
error(self.pos, "Python objects cannot be cast to pointers of primitive types")
else:
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type))
+ warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
+ self.type, self.operand.type))
elif from_py and to_py:
if self.typecheck:
self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
@@ -9885,6 +10722,13 @@
elif self.operand.type.is_fused:
self.operand = self.operand.coerce_to(self.type, env)
#self.type = self.operand.type
+ if self.type.is_ptr and self.type.base_type.is_cfunction and self.type.base_type.nogil:
+ op_type = self.operand.type
+ if op_type.is_ptr:
+ op_type = op_type.base_type
+ if op_type.is_cfunction and not op_type.nogil:
+ warning(self.pos,
+ "Casting a GIL-requiring function into a nogil function circumvents GIL validation", 1)
return self
def is_simple(self):
@@ -10091,7 +10935,7 @@
def allocate_temp_result(self, code):
if self.temp_code:
- raise RuntimeError("temp allocated mulitple times")
+ raise RuntimeError("temp allocated multiple times")
self.temp_code = code.funcstate.allocate_temp(self.type, True)
@@ -10099,7 +10943,9 @@
return self.get_cython_array_type(env)
def get_cython_array_type(self, env):
- return env.global_scope().context.cython_scope.viewscope.lookup("array").type
+ cython_scope = env.global_scope().context.cython_scope
+ cython_scope.load_cythonscope()
+ return cython_scope.viewscope.lookup("array").type
def generate_result_code(self, code):
from . import Buffer
@@ -10121,26 +10967,28 @@
code.putln(code.error_goto(self.operand.pos))
code.putln("}")
- code.putln("%s = __pyx_format_from_typeinfo(&%s);" %
- (format_temp, type_info))
+ code.putln("%s = __pyx_format_from_typeinfo(&%s); %s" % (
+ format_temp,
+ type_info,
+ code.error_goto_if_null(format_temp, self.pos),
+ ))
+ code.put_gotref(format_temp, py_object_type)
+
buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
- code.putln('%s = Py_BuildValue((char*) "(" %s ")", %s);' % (
- shapes_temp, buildvalue_fmt, ", ".join(shapes)))
+ code.putln('%s = Py_BuildValue((char*) "(" %s ")", %s); %s' % (
+ shapes_temp,
+ buildvalue_fmt,
+ ", ".join(shapes),
+ code.error_goto_if_null(shapes_temp, self.pos),
+ ))
+ code.put_gotref(shapes_temp, py_object_type)
- err = "!%s || !%s || !PyBytes_AsString(%s)" % (format_temp,
- shapes_temp,
- format_temp)
- code.putln(code.error_goto_if(err, self.pos))
- code.put_gotref(format_temp)
- code.put_gotref(shapes_temp)
-
- tup = (self.result(), shapes_temp, itemsize, format_temp,
- self.mode, self.operand.result())
- code.putln('%s = __pyx_array_new('
- '%s, %s, PyBytes_AS_STRING(%s), '
- '(char *) "%s", (char *) %s);' % tup)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.result())
+ code.putln('%s = __pyx_array_new(%s, %s, PyBytes_AS_STRING(%s), (char *) "%s", (char *) %s); %s' % (
+ self.result(),
+ shapes_temp, itemsize, format_temp, self.mode, self.operand.result(),
+ code.error_goto_if_null(self.result(), self.pos),
+ ))
+ self.generate_gotref(code)
def dispose(temp):
code.put_decref_clear(temp, py_object_type)
@@ -10205,9 +11053,7 @@
for attr in path[1:]:
operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
- self.operand = operand
- self.__class__ = SizeofVarNode
- node = self.analyse_types(env)
+ node = SizeofVarNode(self.pos, operand=operand).analyse_types(env)
return node
if self.arg_type is None:
base_type = self.base_type.analyse(env)
@@ -10251,7 +11097,11 @@
if operand_as_type:
self.arg_type = operand_as_type
if self.arg_type.is_fused:
- self.arg_type = self.arg_type.specialize(env.fused_to_specific)
+ try:
+ self.arg_type = self.arg_type.specialize(env.fused_to_specific)
+ except CannotSpecialize:
+ error(self.operand.pos,
+ "Type cannot be specialized since it is not a fused argument to this function")
self.__class__ = SizeofTypeNode
self.check_type()
else:
@@ -10272,8 +11122,6 @@
# arg_type ExprNode
# is_variable boolean
- type = PyrexTypes.error_type
-
subexprs = ['operand']
arg_type = None
@@ -10288,19 +11136,28 @@
typeinfo_entry = typeinfo_module.lookup('type_info')
return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type))
+ cpp_message = 'typeid operator'
+
def analyse_types(self, env):
+ if not self.type:
+ self.type = PyrexTypes.error_type # default value if it isn't analysed successfully
+ self.cpp_check(env)
type_info = self.get_type_info_type(env)
if not type_info:
self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
return self
+ if self.operand is None:
+ return self # already analysed, no need to repeat
self.type = type_info
- as_type = self.operand.analyse_as_type(env)
+ as_type = self.operand.analyse_as_specialized_type(env)
if as_type:
self.arg_type = as_type
self.is_type = True
+ self.operand = None # nothing further uses self.operand - will only cause problems if its used in code generation
else:
self.arg_type = self.operand.analyse_types(env)
self.is_type = False
+ self.operand = None # nothing further uses self.operand - will only cause problems if its used in code generation
if self.arg_type.type.is_pyobject:
self.error("Cannot use typeid on a Python object")
return self
@@ -10331,7 +11188,7 @@
arg_code = self.arg_type.result()
translate_cpp_exception(code, self.pos,
"%s = typeid(%s);" % (self.temp_code, arg_code),
- None, self.in_nogil_context)
+ None, None, self.in_nogil_context)
class TypeofNode(ExprNode):
# Compile-time type of an expression, as a string.
@@ -10342,16 +11199,20 @@
literal = None
type = py_object_type
- subexprs = ['literal'] # 'operand' will be ignored after type analysis!
+ subexprs = ['literal'] # 'operand' will be ignored after type analysis!
def analyse_types(self, env):
self.operand = self.operand.analyse_types(env)
- value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
+ value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
literal = StringNode(self.pos, value=value)
literal = literal.analyse_types(env)
self.literal = literal.coerce_to_pyobject(env)
return self
+ def analyse_as_type(self, env):
+ self.operand = self.operand.analyse_types(env)
+ return self.operand.type
+
def may_be_none(self):
return False
@@ -10445,7 +11306,7 @@
def infer_type(self, env):
return self.result_type(self.operand1.infer_type(env),
- self.operand2.infer_type(env))
+ self.operand2.infer_type(env), env)
def analyse_types(self, env):
self.operand1 = self.operand1.analyse_types(env)
@@ -10454,10 +11315,15 @@
return self
def analyse_operation(self, env):
- if self.is_py_operation():
+ if self.is_pythran_operation(env):
+ self.type = self.result_type(self.operand1.type,
+ self.operand2.type, env)
+ assert self.type.is_pythran_expr
+ self.is_temp = 1
+ elif self.is_py_operation():
self.coerce_operands_to_pyobjects(env)
self.type = self.result_type(self.operand1.type,
- self.operand2.type)
+ self.operand2.type, env)
assert self.type.is_pyobject
self.is_temp = 1
elif self.is_cpp_operation():
@@ -10471,6 +11337,15 @@
def is_py_operation_types(self, type1, type2):
return type1.is_pyobject or type2.is_pyobject or type1.is_ctuple or type2.is_ctuple
+ def is_pythran_operation(self, env):
+ return self.is_pythran_operation_types(self.operand1.type, self.operand2.type, env)
+
+ def is_pythran_operation_types(self, type1, type2, env):
+ # Support only expr op supported_type, or supported_type op expr
+ return has_np_pythran(env) and \
+ (is_pythran_supported_operation_type(type1) and is_pythran_supported_operation_type(type2)) and \
+ (is_pythran_expr(type1) or is_pythran_expr(type2))
+
def is_cpp_operation(self):
return (self.operand1.type.is_cpp_class
or self.operand2.type.is_cpp_class)
@@ -10487,7 +11362,7 @@
# Used by NumBinopNodes to break up expressions involving multiple
# operators so that exceptions can be handled properly.
self.is_temp = 1
- if self.exception_value is None:
+ if needs_cpp_exception_conversion(self):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
if func_type.is_ptr:
func_type = func_type.base_type
@@ -10498,7 +11373,9 @@
self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
self.type = func_type.return_type
- def result_type(self, type1, type2):
+ def result_type(self, type1, type2, env):
+ if self.is_pythran_operation_types(type1, type2, env):
+ return PythranExpr(pythran_binop_type(self.operator, type1, type2))
if self.is_py_operation_types(type1, type2):
if type2.is_string:
type2 = Builtin.bytes_type
@@ -10516,6 +11393,8 @@
if result_type is not None:
return result_type
return py_object_type
+ elif type1.is_error or type2.is_error:
+ return PyrexTypes.error_type
else:
return self.compute_c_result_type(type1, type2)
@@ -10538,8 +11417,23 @@
self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
def generate_result_code(self, code):
- #print "BinopNode.generate_result_code:", self.operand1, self.operand2 ###
- if self.operand1.type.is_pyobject:
+ if self.type.is_pythran_expr:
+ code.putln("// Pythran binop")
+ code.putln("__Pyx_call_destructor(%s);" % self.result())
+ if self.operator == '**':
+ code.putln("new (&%s) decltype(%s){pythonic::numpy::functor::power{}(%s, %s)};" % (
+ self.result(),
+ self.result(),
+ self.operand1.pythran_result(),
+ self.operand2.pythran_result()))
+ else:
+ code.putln("new (&%s) decltype(%s){%s %s %s};" % (
+ self.result(),
+ self.result(),
+ self.operand1.pythran_result(),
+ self.operator,
+ self.operand2.pythran_result()))
+ elif self.operand1.type.is_pyobject:
function = self.py_operation_function(code)
if self.operator == '**':
extra_args = ", Py_None"
@@ -10553,13 +11447,14 @@
self.operand2.py_result(),
extra_args,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
elif self.is_temp:
# C++ overloaded operators with exception values are currently all
# handled through temporaries.
if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), self.calculate_result_code()),
+ self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
@@ -10594,9 +11489,8 @@
cpp_type = None
if type1.is_cpp_class or type1.is_ptr:
cpp_type = type1.find_cpp_operation_type(self.operator, type2)
- # FIXME: handle the reversed case?
- #if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
- # cpp_type = type2.find_cpp_operation_type(self.operator, type1)
+ if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
+ cpp_type = type2.find_cpp_operation_type(self.operator, type1)
# FIXME: do we need to handle other cases here?
return cpp_type
@@ -10677,8 +11571,8 @@
def c_types_okay(self, type1, type2):
#print "NumBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_numeric or type1.is_enum) \
- and (type2.is_numeric or type2.is_enum)
+ return (type1.is_numeric or type1.is_enum) \
+ and (type2.is_numeric or type2.is_enum)
def generate_evaluation_code(self, code):
if self.overflow_check:
@@ -10701,10 +11595,11 @@
self.operand2.result(),
self.overflow_bit_node.overflow_bit)
elif self.type.is_cpp_class or self.infix:
- return "(%s %s %s)" % (
- self.operand1.result(),
- self.operator,
- self.operand2.result())
+ if is_pythran_expr(self.type):
+ result1, result2 = self.operand1.pythran_result(), self.operand2.pythran_result()
+ else:
+ result1, result2 = self.operand1.result(), self.operand2.result()
+ return "(%s %s %s)" % (result1, self.operator, result2)
else:
func = self.type.binary_op(self.operator)
if func is None:
@@ -10770,7 +11665,7 @@
def infer_builtin_types_operation(self, type1, type2):
# b'abc' + 'abc' raises an exception in Py3,
# so we can safely infer the Py2 type for bytes here
- string_types = (bytes_type, str_type, basestring_type, unicode_type)
+ string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
if type1 in string_types and type2 in string_types:
return string_types[max(string_types.index(type1),
string_types.index(type2))]
@@ -10787,19 +11682,36 @@
self, type1, type2)
def py_operation_function(self, code):
- is_unicode_concat = False
- if isinstance(self.operand1, FormattedValueNode) or isinstance(self.operand2, FormattedValueNode):
- is_unicode_concat = True
- else:
- type1, type2 = self.operand1.type, self.operand2.type
- if type1 is unicode_type or type2 is unicode_type:
- is_unicode_concat = type1.is_builtin_type and type2.is_builtin_type
+ type1, type2 = self.operand1.type, self.operand2.type
+ func = None
+ if type1 is unicode_type or type2 is unicode_type:
+ if type1 in (unicode_type, str_type) and type2 in (unicode_type, str_type):
+ is_unicode_concat = True
+ elif isinstance(self.operand1, FormattedValueNode) or isinstance(self.operand2, FormattedValueNode):
+ # Assume that even if we don't know the second type, it's going to be a string.
+ is_unicode_concat = True
+ else:
+ # Operation depends on the second type.
+ is_unicode_concat = False
+
+ if is_unicode_concat:
+ if self.inplace or self.operand1.is_temp:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("UnicodeConcatInPlace", "ObjectHandling.c"))
+ func = '__Pyx_PyUnicode_Concat'
+ elif type1 is str_type and type2 is str_type:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("StrConcatInPlace", "ObjectHandling.c"))
+ func = '__Pyx_PyStr_Concat'
- if is_unicode_concat:
+ if func:
+ # any necessary utility code will be got by "NumberAdd" in generate_evaluation_code
+ if self.inplace or self.operand1.is_temp:
+ func += 'InPlace' # upper case to indicate unintuitive macro
if self.operand1.may_be_none() or self.operand2.may_be_none():
- return '__Pyx_PyUnicode_ConcatSafe'
- else:
- return '__Pyx_PyUnicode_Concat'
+ func += 'Safe'
+ return func
+
return super(AddNode, self).py_operation_function(code)
@@ -10819,6 +11731,24 @@
class MulNode(NumBinopNode):
# '*' operator.
+ def analyse_types(self, env):
+ # TODO: we could also optimise the case of "[...] * 2 * n", i.e. with an existing 'mult_factor'
+ if self.operand1.is_sequence_constructor and self.operand1.mult_factor is None:
+ operand2 = self.operand2.analyse_types(env)
+ if operand2.type.is_int or operand2.type is long_type:
+ return self.analyse_sequence_mul(env, self.operand1, operand2)
+ elif self.operand2.is_sequence_constructor and self.operand2.mult_factor is None:
+ operand1 = self.operand1.analyse_types(env)
+ if operand1.type.is_int or operand1.type is long_type:
+ return self.analyse_sequence_mul(env, self.operand2, operand1)
+
+ return NumBinopNode.analyse_types(self, env)
+
+ def analyse_sequence_mul(self, env, seq, mult):
+ assert seq.mult_factor is None
+ seq.mult_factor = mult
+ return seq.analyse_types(env)
+
def is_py_operation_types(self, type1, type2):
if ((type1.is_string and type2.is_int) or
(type2.is_string and type1.is_int)):
@@ -10829,7 +11759,7 @@
def infer_builtin_types_operation(self, type1, type2):
# let's assume that whatever builtin type you multiply a string with
# will either return a string of the same type or fail with an exception
- string_types = (bytes_type, str_type, basestring_type, unicode_type)
+ string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
if type1 in string_types and type2.is_builtin_type:
return type1
if type2 in string_types and type1.is_builtin_type:
@@ -10899,7 +11829,7 @@
self._check_truedivision(env)
return self.result_type(
self.operand1.infer_type(env),
- self.operand2.infer_type(env))
+ self.operand2.infer_type(env), env)
def analyse_operation(self, env):
self._check_truedivision(env)
@@ -10917,7 +11847,7 @@
self.operand2 = self.operand2.coerce_to_simple(env)
def compute_c_result_type(self, type1, type2):
- if self.operator == '/' and self.ctruedivision:
+ if self.operator == '/' and self.ctruedivision and not type1.is_cpp_class and not type2.is_cpp_class:
if not type1.is_float and not type2.is_float:
widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
@@ -10933,9 +11863,11 @@
def generate_evaluation_code(self, code):
if not self.type.is_pyobject and not self.type.is_complex:
if self.cdivision is None:
- self.cdivision = (code.globalstate.directives['cdivision']
- or not self.type.signed
- or self.type.is_float)
+ self.cdivision = (
+ code.globalstate.directives['cdivision']
+ or self.type.is_float
+ or ((self.type.is_numeric or self.type.is_enum) and not self.type.signed)
+ )
if not self.cdivision:
code.globalstate.use_utility_code(
UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type))
@@ -10968,7 +11900,7 @@
minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
type_of_op2, self.operand2.result(), type_of_op2)
code.putln("else if (sizeof(%s) == sizeof(long) && %s "
- " && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
+ " && unlikely(__Pyx_UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
self.type.empty_declaration_code(),
minus1_check,
self.operand1.result()))
@@ -11006,7 +11938,7 @@
code.putln("}")
def calculate_result_code(self):
- if self.type.is_complex:
+ if self.type.is_complex or self.is_cpp_operation():
return NumBinopNode.calculate_result_code(self)
elif self.type.is_float and self.operator == '//':
return "floor(%s / %s)" % (
@@ -11028,6 +11960,20 @@
self.operand2.result())
+_find_formatting_types = re.compile(
+ br"%"
+ br"(?:%|" # %%
+ br"(?:\([^)]+\))?" # %(name)
+ br"[-+#,0-9 ]*([a-z])" # %.2f etc.
+ br")").findall
+
+# These format conversion types can never trigger a Unicode string conversion in Py2.
+_safe_bytes_formats = frozenset({
+ # Excludes 's' and 'r', which can generate non-bytes strings.
+ b'd', b'i', b'o', b'u', b'x', b'X', b'e', b'E', b'f', b'F', b'g', b'G', b'c', b'b', b'a',
+})
+
+
class ModNode(DivNode):
# '%' operator.
@@ -11037,7 +11983,7 @@
or NumBinopNode.is_py_operation_types(self, type1, type2))
def infer_builtin_types_operation(self, type1, type2):
- # b'%s' % xyz raises an exception in Py3, so it's safe to infer the type for Py2
+ # b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for Py2 and later Py3's.
if type1 is unicode_type:
# None + xyz may be implemented by RHS
if type2.is_builtin_type or not self.operand1.may_be_none():
@@ -11047,6 +11993,11 @@
return type2
elif type2.is_numeric:
return type1
+ elif self.operand1.is_string_literal:
+ if type1 is str_type or type1 is bytes_type:
+ if set(_find_formatting_types(self.operand1.value)) <= _safe_bytes_formats:
+ return type1
+ return basestring_type
elif type1 is bytes_type and not type2.is_builtin_type:
return None # RHS might implement '% operator differently in Py3
else:
@@ -11098,13 +12049,19 @@
self.operand2.result())
def py_operation_function(self, code):
- if self.operand1.type is unicode_type:
- if self.operand1.may_be_none():
+ type1, type2 = self.operand1.type, self.operand2.type
+ # ("..." % x) must call "x.__rmod__()" for string subtypes.
+ if type1 is unicode_type:
+ if self.operand1.may_be_none() or (
+ type2.is_extension_type and type2.subtype_of(type1) or
+ type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
return '__Pyx_PyUnicode_FormatSafe'
else:
return 'PyUnicode_Format'
- elif self.operand1.type is str_type:
- if self.operand1.may_be_none():
+ elif type1 is str_type:
+ if self.operand1.may_be_none() or (
+ type2.is_extension_type and type2.subtype_of(type1) or
+ type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
return '__Pyx_PyString_FormatSafe'
else:
return '__Pyx_PyString_Format'
@@ -11137,6 +12094,12 @@
error(self.pos, "got unexpected types for C power operator: %s, %s" %
(self.operand1.type, self.operand2.type))
+ def compute_c_result_type(self, type1, type2):
+ c_result_type = super(PowNode, self).compute_c_result_type(type1, type2)
+ if isinstance(self.operand2.constant_result, _py_int_types) and self.operand2.constant_result < 0:
+ c_result_type = PyrexTypes.widest_numeric_type(c_result_type, PyrexTypes.c_double_type)
+ return c_result_type
+
def calculate_result_code(self):
# Work around MSVC overloading ambiguity.
def typecast(operand):
@@ -11245,7 +12208,7 @@
operator=self.operator,
operand1=operand1, operand2=operand2)
- def generate_bool_evaluation_code(self, code, final_result_temp, and_label, or_label, end_label, fall_through):
+ def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
code.mark_pos(self.pos)
outer_labels = (and_label, or_label)
@@ -11254,19 +12217,20 @@
else:
my_label = or_label = code.new_label('next_or')
self.operand1.generate_bool_evaluation_code(
- code, final_result_temp, and_label, or_label, end_label, my_label)
+ code, final_result_temp, final_result_type, and_label, or_label, end_label, my_label)
and_label, or_label = outer_labels
code.put_label(my_label)
self.operand2.generate_bool_evaluation_code(
- code, final_result_temp, and_label, or_label, end_label, fall_through)
+ code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through)
def generate_evaluation_code(self, code):
self.allocate_temp_result(code)
+ result_type = PyrexTypes.py_object_type if self.type.is_pyobject else self.type
or_label = and_label = None
end_label = code.new_label('bool_binop_done')
- self.generate_bool_evaluation_code(code, self.result(), and_label, or_label, end_label, end_label)
+ self.generate_bool_evaluation_code(code, self.result(), result_type, and_label, or_label, end_label, end_label)
code.put_label(end_label)
gil_message = "Truth-testing Python object"
@@ -11351,7 +12315,7 @@
test_result = self.arg.result()
return (test_result, self.arg.type.is_pyobject)
- def generate_bool_evaluation_code(self, code, final_result_temp, and_label, or_label, end_label, fall_through):
+ def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
code.mark_pos(self.pos)
# x => x
@@ -11394,7 +12358,7 @@
code.putln("} else {")
self.value.generate_evaluation_code(code)
self.value.make_owned_reference(code)
- code.putln("%s = %s;" % (final_result_temp, self.value.result()))
+ code.putln("%s = %s;" % (final_result_temp, self.value.result_as(final_result_type)))
self.value.generate_post_assignment_code(code)
# disposal: {not (and_label and or_label) [else]}
self.arg.generate_disposal_code(code)
@@ -11406,6 +12370,9 @@
code.putln("}")
self.arg.free_temps(code)
+ def analyse_types(self, env):
+ return self
+
class CondExprNode(ExprNode):
# Short-circuiting conditional expression.
@@ -11416,6 +12383,7 @@
true_val = None
false_val = None
+ is_temp = True
subexprs = ['test', 'true_val', 'false_val']
@@ -11440,7 +12408,6 @@
self.test = self.test.analyse_types(env).coerce_to_boolean(env)
self.true_val = self.true_val.analyse_types(env)
self.false_val = self.false_val.analyse_types(env)
- self.is_temp = 1
return self.analyse_result_type(env)
def analyse_result_type(self, env):
@@ -11614,22 +12581,22 @@
new_common_type = None
# catch general errors
- if type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or \
- type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type)):
+ if (type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or
+ type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type))):
error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
new_common_type = error_type
# try to use numeric comparisons where possible
elif type1.is_complex or type2.is_complex:
- if op not in ('==', '!=') \
- and (type1.is_complex or type1.is_numeric) \
- and (type2.is_complex or type2.is_numeric):
+ if (op not in ('==', '!=')
+ and (type1.is_complex or type1.is_numeric)
+ and (type2.is_complex or type2.is_numeric)):
error(self.pos, "complex types are unordered")
new_common_type = error_type
elif type1.is_pyobject:
- new_common_type = type1
+ new_common_type = Builtin.complex_type if type1.subtype_of(Builtin.complex_type) else py_object_type
elif type2.is_pyobject:
- new_common_type = type2
+ new_common_type = Builtin.complex_type if type2.subtype_of(Builtin.complex_type) else py_object_type
else:
new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
elif type1.is_numeric and type2.is_numeric:
@@ -11755,6 +12722,11 @@
self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
self.special_bool_cmp_function = "__Pyx_PyDict_ContainsTF"
return True
+ elif self.operand2.type is Builtin.set_type:
+ self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySetContains", "ObjectHandling.c")
+ self.special_bool_cmp_function = "__Pyx_PySet_ContainsTF"
+ return True
elif self.operand2.type is Builtin.unicode_type:
self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
@@ -11842,8 +12814,15 @@
self.c_operator(op),
code2)
if self.is_cpp_comparison() and self.exception_check == '+':
- translate_cpp_exception(code, self.pos, statement, self.exception_value, self.in_nogil_context)
- code.putln(statement)
+ translate_cpp_exception(
+ code,
+ self.pos,
+ statement,
+ result_code if self.type.is_pyobject else None,
+ self.exception_value,
+ self.in_nogil_context)
+ else:
+ code.putln(statement)
def c_operator(self, op):
if op == 'is':
@@ -11874,7 +12853,14 @@
is_memslice_nonecheck = False
def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
+ type1 = self.operand1.infer_type(env)
+ type2 = self.operand2.infer_type(env)
+
+ if is_pythran_expr(type1) or is_pythran_expr(type2):
+ if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
+ return PythranExpr(pythran_binop_type(self.operator, type1, type2))
+
+ # TODO: implement this for other types.
return py_object_type
def type_dependencies(self, env):
@@ -11897,6 +12883,14 @@
error(self.pos, "Cascading comparison not yet supported for cpp types.")
return self
+ type1 = self.operand1.type
+ type2 = self.operand2.type
+ if is_pythran_expr(type1) or is_pythran_expr(type2):
+ if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
+ self.type = PythranExpr(pythran_binop_type(self.operator, type1, type2))
+ self.is_pycmp = False
+ return self
+
if self.analyse_memoryviewslice_comparison(env):
return self
@@ -11929,16 +12923,16 @@
elif self.find_special_bool_compare_function(env, self.operand1):
if not self.operand1.type.is_pyobject:
self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
+ common_type = None # if coercion needed, the method call above has already done it
+ self.is_pycmp = False # result is bint
else:
common_type = py_object_type
self.is_pycmp = True
elif self.find_special_bool_compare_function(env, self.operand1):
if not self.operand1.type.is_pyobject:
self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
+ common_type = None # if coercion needed, the method call above has already done it
+ self.is_pycmp = False # result is bint
else:
common_type = self.find_common_type(env, self.operator, self.operand1)
self.is_pycmp = common_type.is_pyobject
@@ -11985,7 +12979,7 @@
self.exception_value = func_type.exception_value
if self.exception_check == '+':
self.is_temp = True
- if self.exception_value is None:
+ if needs_cpp_exception_conversion(self):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
if len(func_type.args) == 1:
self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
@@ -12036,18 +13030,19 @@
return self.operand1.check_const() and self.operand2.check_const()
def calculate_result_code(self):
- if self.operand1.type.is_complex:
+ operand1, operand2 = self.operand1, self.operand2
+ if operand1.type.is_complex:
if self.operator == "!=":
negation = "!"
else:
negation = ""
return "(%s%s(%s, %s))" % (
negation,
- self.operand1.type.binary_op('=='),
- self.operand1.result(),
- self.operand2.result())
+ operand1.type.binary_op('=='),
+ operand1.result(),
+ operand2.result())
elif self.is_c_string_contains():
- if self.operand2.type is unicode_type:
+ if operand2.type is unicode_type:
method = "__Pyx_UnicodeContainsUCS4"
else:
method = "__Pyx_BytesContains"
@@ -12058,16 +13053,18 @@
return "(%s%s(%s, %s))" % (
negation,
method,
- self.operand2.result(),
- self.operand1.result())
+ operand2.result(),
+ operand1.result())
else:
- result1 = self.operand1.result()
- result2 = self.operand2.result()
- if self.is_memslice_nonecheck:
- if self.operand1.type.is_memoryviewslice:
- result1 = "((PyObject *) %s.memview)" % result1
- else:
- result2 = "((PyObject *) %s.memview)" % result2
+ if is_pythran_expr(self.type):
+ result1, result2 = operand1.pythran_result(), operand2.pythran_result()
+ else:
+ result1, result2 = operand1.result(), operand2.result()
+ if self.is_memslice_nonecheck:
+ if operand1.type.is_memoryviewslice:
+ result1 = "((PyObject *) %s.memview)" % result1
+ else:
+ result2 = "((PyObject *) %s.memview)" % result2
return "(%s %s %s)" % (
result1,
@@ -12123,7 +13120,7 @@
cascade = None
coerced_operand2 = None
- constant_result = constant_value_not_set # FIXME: where to calculate this?
+ constant_result = constant_value_not_set # FIXME: where to calculate this?
def infer_type(self, env):
# TODO: Actually implement this (after merging with -unstable).
@@ -12270,6 +13267,9 @@
code.annotate((file, line, col-1), AnnotationItem(
style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
+ def analyse_types(self, env):
+ return self
+
class CoerceToMemViewSliceNode(CoercionNode):
"""
@@ -12283,18 +13283,17 @@
CoercionNode.__init__(self, arg)
self.type = dst_type
self.is_temp = 1
- self.env = env
self.use_managed_ref = True
self.arg = arg
+ self.type.create_from_py_utility_code(env)
def generate_result_code(self, code):
- self.type.create_from_py_utility_code(self.env)
- code.putln("%s = %s(%s);" % (self.result(),
- self.type.from_py_function,
- self.arg.py_result()))
-
- error_cond = self.type.error_condition(self.result())
- code.putln(code.error_goto_if(error_cond, self.pos))
+ code.putln(self.type.from_py_call_code(
+ self.arg.py_result(),
+ self.result(),
+ self.pos,
+ code
+ ))
class CastNode(CoercionNode):
@@ -12322,9 +13321,10 @@
exact_builtin_type = True
def __init__(self, arg, dst_type, env, notnone=False):
- # The arg is know to be a Python object, and
+ # The arg is known to be a Python object, and
# the dst_type is known to be an extension type.
- assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
+ assert dst_type.is_extension_type or dst_type.is_builtin_type, \
+ "PyTypeTest for %s against non extension type %s" % (arg.type, dst_type)
CoercionNode.__init__(self, arg)
self.type = dst_type
self.result_ctype = arg.ctype()
@@ -12353,6 +13353,15 @@
def nonlocally_immutable(self):
return self.arg.nonlocally_immutable()
+ def reanalyse(self):
+ if self.type != self.arg.type or not self.arg.is_temp:
+ return self
+ if not self.type.typeobj_is_available():
+ return self
+ if self.arg.may_be_none() and self.notnone:
+ return self.arg.as_none_safe_node("Cannot convert NoneType to %.200s" % self.type.name)
+ return self.arg
+
def calculate_constant_result(self):
# FIXME
pass
@@ -12366,6 +13375,8 @@
type_test = self.type.type_test_code(
self.arg.py_result(),
self.notnone, exact=self.exact_builtin_type)
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "RaiseUnexpectedTypeError", "ObjectHandling.c"))
else:
type_test = self.type.type_test_code(
self.arg.py_result(), self.notnone)
@@ -12380,9 +13391,18 @@
def generate_post_assignment_code(self, code):
self.arg.generate_post_assignment_code(code)
+ def allocate_temp_result(self, code):
+ pass
+
+ def release_temp_result(self, code):
+ pass
+
def free_temps(self, code):
self.arg.free_temps(code)
+ def free_subexpr_temps(self, code):
+ self.arg.free_subexpr_temps(code)
+
class NoneCheckNode(CoercionNode):
# This node is used to check that a Python object is not None and
@@ -12392,7 +13412,7 @@
is_nonecheck = True
def __init__(self, arg, exception_type_cname, exception_message,
- exception_format_args):
+ exception_format_args=()):
CoercionNode.__init__(self, arg)
self.type = arg.type
self.result_ctype = arg.ctype()
@@ -12400,7 +13420,7 @@
self.exception_message = exception_message
self.exception_format_args = tuple(exception_format_args or ())
- nogil_check = None # this node only guards an operation that would fail already
+ nogil_check = None # this node only guards an operation that would fail already
def analyse_types(self, env):
return self
@@ -12428,6 +13448,19 @@
else:
raise Exception("unsupported type")
+ @classmethod
+ def generate(cls, arg, code, exception_message,
+ exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
+ node = cls(arg, exception_type_cname, exception_message, exception_format_args)
+ node.in_nogil_context = in_nogil_context
+ node.put_nonecheck(code)
+
+ @classmethod
+ def generate_if_needed(cls, arg, code, exception_message,
+ exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
+ if arg.may_be_none():
+ cls.generate(arg, code, exception_message, exception_type_cname, exception_format_args, in_nogil_context)
+
def put_nonecheck(self, code):
code.putln(
"if (unlikely(%s == Py_None)) {" % self.condition())
@@ -12510,7 +13543,7 @@
def coerce_to_boolean(self, env):
arg_type = self.arg.type
if (arg_type == PyrexTypes.c_bint_type or
- (arg_type.is_pyobject and arg_type.name == 'bool')):
+ (arg_type.is_pyobject and arg_type.name == 'bool')):
return self.arg.coerce_to_temp(env)
else:
return CoerceToBooleanNode(self, env)
@@ -12534,7 +13567,7 @@
self.target_type),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class CoerceIntToBytesNode(CoerceToPyTypeNode):
@@ -12574,7 +13607,7 @@
code.error_goto_if_null(self.result(), self.pos)))
if temp is not None:
code.funcstate.release_temp(temp)
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
class CoerceFromPyTypeNode(CoercionNode):
@@ -12602,10 +13635,17 @@
return (self.type.is_ptr and not self.type.is_array) and self.arg.is_ephemeral()
def generate_result_code(self, code):
+ from_py_function = None
+ # for certain source types, we can do better than the generic coercion
+ if self.type.is_string and self.arg.type is bytes_type:
+ if self.type.from_py_function.startswith('__Pyx_PyObject_As'):
+ from_py_function = '__Pyx_PyBytes' + self.type.from_py_function[len('__Pyx_PyObject'):]
+ NoneCheckNode.generate_if_needed(self.arg, code, "expected bytes, NoneType found")
+
code.putln(self.type.from_py_call_code(
- self.arg.py_result(), self.result(), self.pos, code))
+ self.arg.py_result(), self.result(), self.pos, code, from_py_function=from_py_function))
if self.type.is_pyobject:
- code.put_gotref(self.py_result())
+ self.generate_gotref(code)
def nogil_check(self, env):
error(self.pos, "Coercion from Python not allowed without the GIL")
@@ -12623,6 +13663,7 @@
Builtin.set_type: 'PySet_GET_SIZE',
Builtin.frozenset_type: 'PySet_GET_SIZE',
Builtin.bytes_type: 'PyBytes_GET_SIZE',
+ Builtin.bytearray_type: 'PyByteArray_GET_SIZE',
Builtin.unicode_type: '__Pyx_PyUnicode_IS_TRUE',
}
@@ -12651,11 +13692,9 @@
return
test_func = self._special_builtins.get(self.arg.type)
if test_func is not None:
- code.putln("%s = (%s != Py_None) && (%s(%s) != 0);" % (
- self.result(),
- self.arg.py_result(),
- test_func,
- self.arg.py_result()))
+ checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else []
+ checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result()))
+ code.putln("%s = %s;" % (self.result(), '&&'.join(checks)))
else:
code.putln(
"%s = __Pyx_PyObject_IsTrue(%s); %s" % (
@@ -12663,6 +13702,9 @@
self.arg.py_result(),
code.error_goto_if_neg(self.result(), self.pos)))
+ def analyse_types(self, env):
+ return self
+
class CoerceToComplexNode(CoercionNode):
@@ -12688,6 +13730,9 @@
def generate_result_code(self, code):
pass
+ def analyse_types(self, env):
+ return self
+
class CoerceToTempNode(CoercionNode):
# This node is used to force the result of another node
# to be stored in a temporary. It is only used if the
@@ -12707,6 +13752,9 @@
# The arg is always already analysed
return self
+ def may_be_none(self):
+ return self.arg.may_be_none()
+
def coerce_to_boolean(self, env):
self.arg = self.arg.coerce_to_boolean(env)
if self.arg.is_simple():
@@ -12721,11 +13769,12 @@
code.putln("%s = %s;" % (
self.result(), self.arg.result_as(self.ctype())))
if self.use_managed_ref:
- if self.type.is_pyobject:
+ if not self.type.is_memoryviewslice:
code.put_incref(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_incref_memoryviewslice(self.result(),
- not self.in_nogil_context)
+ else:
+ code.put_incref_memoryviewslice(self.result(), self.type,
+ have_gil=not self.in_nogil_context)
+
class ProxyNode(CoercionNode):
"""
@@ -12741,22 +13790,24 @@
def __init__(self, arg):
super(ProxyNode, self).__init__(arg)
self.constant_result = arg.constant_result
- self._proxy_type()
+ self.update_type_and_entry()
def analyse_types(self, env):
self.arg = self.arg.analyse_expressions(env)
- self._proxy_type()
+ self.update_type_and_entry()
return self
def infer_type(self, env):
return self.arg.infer_type(env)
- def _proxy_type(self):
- if hasattr(self.arg, 'type'):
- self.type = self.arg.type
+ def update_type_and_entry(self):
+ type = getattr(self.arg, 'type', None)
+ if type:
+ self.type = type
self.result_ctype = self.arg.result_ctype
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
+ arg_entry = getattr(self.arg, 'entry', None)
+ if arg_entry:
+ self.entry = arg_entry
def generate_result_code(self, code):
self.arg.generate_result_code(code)
@@ -12787,17 +13838,19 @@
# disposal code for it. The original owner of the argument
# node is responsible for doing those things.
- subexprs = [] # Arg is not considered a subexpr
+ subexprs = [] # Arg is not considered a subexpr
nogil_check = None
def __init__(self, arg):
CoercionNode.__init__(self, arg)
self.constant_result = arg.constant_result
- if hasattr(arg, 'type'):
- self.type = arg.type
+ type = getattr(arg, 'type', None)
+ if type:
+ self.type = type
self.result_ctype = arg.result_ctype
- if hasattr(arg, 'entry'):
- self.entry = arg.entry
+ arg_entry = getattr(arg, 'entry', None)
+ if arg_entry:
+ self.entry = arg_entry
def result(self):
return self.arg.result()
@@ -12815,8 +13868,9 @@
self.type = self.arg.type
self.result_ctype = self.arg.result_ctype
self.is_temp = 1
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
+ arg_entry = getattr(self.arg, 'entry', None)
+ if arg_entry:
+ self.entry = arg_entry
return self
def coerce_to(self, dest_type, env):
@@ -12825,7 +13879,7 @@
return super(CloneNode, self).coerce_to(dest_type, env)
def is_simple(self):
- return True # result is always in a temp (or a name)
+ return True # result is always in a temp (or a name)
def generate_evaluation_code(self, code):
pass
@@ -12836,10 +13890,38 @@
def generate_disposal_code(self, code):
pass
+ def generate_post_assignment_code(self, code):
+ # if we're assigning from a CloneNode then it's "giveref"ed away, so it does
+ # need a matching incref (ideally this should happen before the assignment though)
+ if self.is_temp: # should usually be true
+ code.put_incref(self.result(), self.ctype())
+
def free_temps(self, code):
pass
+class CppOptionalTempCoercion(CoercionNode):
+ """
+ Used only in CoerceCppTemps - handles cases the temp is actually a OptionalCppClassType (and thus needs dereferencing when on the rhs)
+ """
+ is_temp = False
+
+ @property
+ def type(self):
+ return self.arg.type
+
+ def calculate_result_code(self):
+ return "(*%s)" % self.arg.result()
+
+ def generate_result_code(self, code):
+ pass
+
+ def _make_move_result_rhs(self, result, optional=False):
+ # this wouldn't normally get moved (because it isn't a temp), but force it to be because it
+ # is a thin wrapper around a temp
+ return super(CppOptionalTempCoercion, self)._make_move_result_rhs(result, optional=False)
+
+
class CMethodSelfCloneNode(CloneNode):
# Special CloneNode for the self argument of builtin C methods
# that accepts subtypes of the builtin type. This is safe only
@@ -12891,77 +13973,193 @@
self.result(), self.body.result(),
code.intern_identifier(StringEncoding.EncodedString("__doc__")),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
+ self.generate_gotref(code)
+class AnnotationNode(ExprNode):
+ # Deals with the two possible uses of an annotation.
+ # 1. The post PEP-563 use where an annotation is stored
+ # as a string
+ # 2. The Cython use where the annotation can indicate an
+ # object type
+ #
+ # Doesn't handle the pre PEP-563 version where the
+ # annotation is evaluated into a Python Object.
+ subexprs = []
-#------------------------------------------------------------------------------------
-#
-# Runtime support code
-#
-#------------------------------------------------------------------------------------
+ # 'untyped' is set for fused specializations:
+ # Once a fused function has been created we don't want
+ # annotations to override an already set type.
+ untyped = False
-pyerr_occurred_withgil_utility_code= UtilityCode(
-proto = """
-static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
-""",
-impl = """
-static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
- int err;
- #ifdef WITH_THREAD
- PyGILState_STATE _save = PyGILState_Ensure();
- #endif
- err = !!PyErr_Occurred();
- #ifdef WITH_THREAD
- PyGILState_Release(_save);
- #endif
- return err;
-}
-"""
-)
+ def __init__(self, pos, expr, string=None):
+ """string is expected to already be a StringNode or None"""
+ ExprNode.__init__(self, pos)
+ if string is None:
+ # import doesn't work at top of file?
+ from .AutoDocTransforms import AnnotationWriter
+ string = StringEncoding.EncodedString(
+ AnnotationWriter(description="annotation").write(expr))
+ string = StringNode(pos, unicode_value=string, value=string.as_utf8_string())
+ self.string = string
+ self.expr = expr
-#------------------------------------------------------------------------------------
+ def analyse_types(self, env):
+ return self # nothing needs doing
-raise_unbound_local_error_utility_code = UtilityCode(
-proto = """
-static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
-""",
-impl = """
-static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
- PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
-}
-""")
+ def analyse_as_type(self, env):
+ # for compatibility when used as a return_type_node, have this interface too
+ return self.analyse_type_annotation(env)[1]
-raise_closure_name_error_utility_code = UtilityCode(
-proto = """
-static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);
-""",
-impl = """
-static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
- PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
-}
-""")
+ def analyse_type_annotation(self, env, assigned_value=None):
+ if self.untyped:
+ # Already applied as a fused type, not re-evaluating it here.
+ return None, None
+ annotation = self.expr
+ base_type = None
+ is_ambiguous = False
+ explicit_pytype = explicit_ctype = False
+ if annotation.is_dict_literal:
+ warning(annotation.pos,
+ "Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly.", level=1)
+ for name, value in annotation.key_value_pairs:
+ if not name.is_string_literal:
+ continue
+ if name.value in ('type', b'type'):
+ explicit_pytype = True
+ if not explicit_ctype:
+ annotation = value
+ elif name.value in ('ctype', b'ctype'):
+ explicit_ctype = True
+ annotation = value
+ if explicit_pytype and explicit_ctype:
+ warning(annotation.pos, "Duplicate type declarations found in signature annotation", level=1)
+ arg_type = annotation.analyse_as_type(env)
+ if annotation.is_name and not annotation.cython_attribute and annotation.name in ('int', 'long', 'float'):
+ # Map builtin numeric Python types to C types in safe cases.
+ if assigned_value is not None and arg_type is not None and not arg_type.is_pyobject:
+ assigned_type = assigned_value.infer_type(env)
+ if assigned_type and assigned_type.is_pyobject:
+ # C type seems unsafe, e.g. due to 'None' default value => ignore annotation type
+ is_ambiguous = True
+ arg_type = None
+ # ignore 'int' and require 'cython.int' to avoid unsafe integer declarations
+ if arg_type in (PyrexTypes.c_long_type, PyrexTypes.c_int_type, PyrexTypes.c_float_type):
+ arg_type = PyrexTypes.c_double_type if annotation.name == 'float' else py_object_type
+ elif arg_type is not None and annotation.is_string_literal:
+ warning(annotation.pos,
+ "Strings should no longer be used for type declarations. Use 'cython.int' etc. directly.",
+ level=1)
+ elif arg_type is not None and arg_type.is_complex:
+ # creating utility code needs to be special-cased for complex types
+ arg_type.create_declaration_utility_code(env)
+ if arg_type is not None:
+ if explicit_pytype and not explicit_ctype and not arg_type.is_pyobject:
+ warning(annotation.pos,
+ "Python type declaration in signature annotation does not refer to a Python type")
+ base_type = Nodes.CAnalysedBaseTypeNode(
+ annotation.pos, type=arg_type, is_arg=True)
+ elif is_ambiguous:
+ warning(annotation.pos, "Ambiguous types in annotation, ignoring")
+ else:
+ warning(annotation.pos, "Unknown type declaration in annotation, ignoring")
+ return base_type, arg_type
-# Don't inline the function, it should really never be called in production
-raise_unbound_memoryview_utility_code_nogil = UtilityCode(
-proto = """
-static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);
-""",
-impl = """
-static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
- #ifdef WITH_THREAD
- PyGILState_STATE gilstate = PyGILState_Ensure();
- #endif
- __Pyx_RaiseUnboundLocalError(varname);
- #ifdef WITH_THREAD
- PyGILState_Release(gilstate);
- #endif
-}
-""",
-requires = [raise_unbound_local_error_utility_code])
-#------------------------------------------------------------------------------------
+class AssignmentExpressionNode(ExprNode):
+ """
+ Also known as a named expression or the walrus operator
+
+ Arguments
+ lhs - NameNode - not stored directly as an attribute of the node
+ rhs - ExprNode
+
+ Attributes
+ rhs - ExprNode
+ assignment - SingleAssignmentNode
+ """
+ # subexprs and child_attrs are intentionally different here, because the assignment is not an expression
+ subexprs = ["rhs"]
+ child_attrs = ["rhs", "assignment"] # This order is important for control-flow (i.e. xdecref) to be right
+
+ is_temp = False
+ assignment = None
+ clone_node = None
+
+ def __init__(self, pos, lhs, rhs, **kwds):
+ super(AssignmentExpressionNode, self).__init__(pos, **kwds)
+ self.rhs = ProxyNode(rhs)
+ assign_expr_rhs = CloneNode(self.rhs)
+ self.assignment = SingleAssignmentNode(
+ pos, lhs=lhs, rhs=assign_expr_rhs, is_assignment_expression=True)
+
+ @property
+ def type(self):
+ return self.rhs.type
+
+ @property
+ def target_name(self):
+ return self.assignment.lhs.name
+
+ def infer_type(self, env):
+ return self.rhs.infer_type(env)
+
+ def analyse_declarations(self, env):
+ self.assignment.analyse_declarations(env)
+
+ def analyse_types(self, env):
+ # we're trying to generate code that looks roughly like:
+ # __pyx_t_1 = rhs
+ # lhs = __pyx_t_1
+ # __pyx_t_1
+ # (plus any reference counting that's needed)
+
+ self.rhs = self.rhs.analyse_types(env)
+ if not self.rhs.arg.is_temp:
+ if not self.rhs.arg.is_literal:
+ # for anything but the simplest cases (where it can be used directly)
+ # we convert rhs to a temp, because CloneNode requires arg to be a temp
+ self.rhs.arg = self.rhs.arg.coerce_to_temp(env)
+ else:
+ # For literals we can optimize by just using the literal twice
+ #
+ # We aren't including `self.rhs.is_name` in this optimization
+ # because that goes wrong for assignment expressions run in
+ # parallel. e.g. `(a := b) + (b := a + c)`)
+ # This is a special case of https://github.com/cython/cython/issues/4146
+ # TODO - once that's fixed general revisit this code and possibly
+ # use coerce_to_simple
+ self.assignment.rhs = copy.copy(self.rhs)
+
+ # TODO - there's a missed optimization in the code generation stage
+ # for self.rhs.arg.is_temp: an incref/decref pair can be removed
+ # (but needs a general mechanism to do that)
+ self.assignment = self.assignment.analyse_types(env)
+ return self
+
+ def coerce_to(self, dst_type, env):
+ if dst_type == self.assignment.rhs.type:
+ # in this quite common case (for example, when both lhs, and self are being coerced to Python)
+ # we can optimize the coercion out by sharing it between
+ # this and the assignment
+ old_rhs_arg = self.rhs.arg
+ if isinstance(old_rhs_arg, CoerceToTempNode):
+ old_rhs_arg = old_rhs_arg.arg
+ rhs_arg = old_rhs_arg.coerce_to(dst_type, env)
+ if rhs_arg is not old_rhs_arg:
+ self.rhs.arg = rhs_arg
+ self.rhs.update_type_and_entry()
+ # clean up the old coercion node that the assignment has likely generated
+ if (isinstance(self.assignment.rhs, CoercionNode)
+ and not isinstance(self.assignment.rhs, CloneNode)):
+ self.assignment.rhs = self.assignment.rhs.arg
+ self.assignment.rhs.type = self.assignment.rhs.arg.type
+ return self
+ return super(AssignmentExpressionNode, self).coerce_to(dst_type, env)
+
+ def calculate_result_code(self):
+ return self.rhs.result()
-raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
-raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
-tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")
+ def generate_result_code(self, code):
+ # we have to do this manually because it isn't a subexpression
+ self.assignment.generate_execution_code(code)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/FlowControl.pxd cython-0.20.1+1~202203241016-9537/Cython/Compiler/FlowControl.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/FlowControl.pxd 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/FlowControl.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+# cython: language_level=3
cimport cython
@@ -11,10 +11,8 @@
cdef public list stats
cdef public dict gen
cdef public set bounded
- cdef public dict input
- cdef public dict output
- # Big integer it bitsets
+ # Big integer bitsets
cdef public object i_input
cdef public object i_output
cdef public object i_gen
@@ -107,6 +105,7 @@
cdef list stack
cdef object env
cdef ControlFlow flow
+ cdef object object_expr
cdef bint in_inplace_assignment
cpdef mark_assignment(self, lhs, rhs=*)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/FlowControl.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/FlowControl.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/FlowControl.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/FlowControl.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,21 +1,22 @@
+# cython: language_level=3str
+# cython: auto_pickle=True
+
from __future__ import absolute_import
import cython
-cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object,
- Builtin=object, InternalError=object, error=object, warning=object,
- py_object_type=object, unspecified_type=object,
- object_expr=object, fake_rhs_expr=object, TypedExprNode=object)
+cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object, Builtin=object,
+ Options=object, TreeVisitor=object, CythonTransform=object,
+ InternalError=object, error=object, warning=object,
+ fake_rhs_expr=object, TypedExprNode=object)
from . import Builtin
from . import ExprNodes
from . import Nodes
from . import Options
-from .PyrexTypes import py_object_type, unspecified_type
from . import PyrexTypes
from .Visitor import TreeVisitor, CythonTransform
from .Errors import error, warning, InternalError
-from .Optimize import ConstantFolding
class TypedExprNode(ExprNodes.ExprNode):
@@ -28,9 +29,8 @@
def may_be_none(self):
return self._may_be_none != False
-object_expr = TypedExprNode(py_object_type, may_be_none=True)
# Fake rhs to silence "unused variable" warning
-fake_rhs_expr = TypedExprNode(unspecified_type)
+fake_rhs_expr = TypedExprNode(PyrexTypes.unspecified_type)
class ControlBlock(object):
@@ -52,7 +52,7 @@
stats = [Assignment(a), NameReference(a), NameReference(c),
Assignment(b)]
gen = {Entry(a): Assignment(a), Entry(b): Assignment(b)}
- bounded = set([Entry(a), Entry(c)])
+ bounded = {Entry(a), Entry(c)}
"""
@@ -160,7 +160,7 @@
(entry.type.is_struct_or_union or
entry.type.is_complex or
entry.type.is_array or
- entry.type.is_cpp_class)):
+ (entry.type.is_cpp_class and not entry.is_cpp_optional))):
# stack allocated structured variable => never uninitialised
return True
return False
@@ -203,7 +203,7 @@
def normalize(self):
"""Delete unreachable and orphan blocks."""
- queue = set([self.entry_point])
+ queue = {self.entry_point}
visited = set()
while queue:
root = queue.pop()
@@ -217,7 +217,7 @@
visited.remove(self.entry_point)
for block in visited:
if block.empty():
- for parent in block.parents: # Re-parent
+ for parent in block.parents: # Re-parent
for child in block.children:
parent.add_child(child)
block.detach()
@@ -341,14 +341,6 @@
return self.entry.type
return self.inferred_type
- def __getstate__(self):
- return (self.lhs, self.rhs, self.entry, self.pos,
- self.refs, self.is_arg, self.is_deletion, self.inferred_type)
-
- def __setstate__(self, state):
- (self.lhs, self.rhs, self.entry, self.pos,
- self.refs, self.is_arg, self.is_deletion, self.inferred_type) = state
-
class StaticAssignment(NameAssignment):
"""Initialised at declaration time, e.g. stack allocation."""
@@ -381,9 +373,9 @@
def infer_type(self):
inferred_type = self.rhs.infer_type(self.entry.scope)
- if (not inferred_type.is_pyobject and
- inferred_type.can_coerce_to_pyobject(self.entry.scope)):
- return py_object_type
+ if (not inferred_type.is_pyobject
+ and inferred_type.can_coerce_to_pyobject(self.entry.scope)):
+ return PyrexTypes.py_object_type
self.inferred_type = inferred_type
return inferred_type
@@ -463,7 +455,7 @@
start = min(block.positions)
stop = max(block.positions)
srcdescr = start[0]
- if not srcdescr in self.sources:
+ if srcdescr not in self.sources:
self.sources[srcdescr] = list(srcdescr.get_lines())
lines = self.sources[srcdescr]
return '\\n'.join([l.strip() for l in lines[start[1] - 1:stop[1]]])
@@ -598,7 +590,7 @@
if (node.allow_null or entry.from_closure
or entry.is_pyclass_attr or entry.type.is_error):
pass # Can be uninitialized here
- elif node.cf_is_null:
+ elif node.cf_is_null and not entry.in_closure:
if entry.error_on_uninitialized or (
Options.error_on_uninitialized and (
entry.type.is_pyobject or entry.type.is_unspecified)):
@@ -612,10 +604,12 @@
"local variable '%s' referenced before assignment"
% entry.name)
elif warn_maybe_uninitialized:
+ msg = "local variable '%s' might be referenced before assignment" % entry.name
+ if entry.in_closure:
+ msg += " (maybe initialized inside a closure)"
messages.warning(
node.pos,
- "local variable '%s' might be referenced before assignment"
- % entry.name)
+ msg)
elif Unknown in node.cf_state:
# TODO: better cross-closure analysis to know when inner functions
# are being called before a variable is being set, and when
@@ -629,7 +623,7 @@
# Unused result
for assmt in assignments:
if (not assmt.refs and not assmt.entry.is_pyclass_attr
- and not assmt.entry.in_closure):
+ and not assmt.entry.in_closure):
if assmt.entry.cf_references and warn_unused_result:
if assmt.is_arg:
messages.warning(assmt.pos, "Unused argument value '%s'" %
@@ -669,7 +663,7 @@
self.assignments = []
def visit_Node(self):
- self._visitchildren(self, None)
+ self._visitchildren(self, None, None)
def visit_SingleAssignmentNode(self, node):
self.assignments.append((node.lhs, node.rhs))
@@ -682,7 +676,10 @@
class ControlFlowAnalysis(CythonTransform):
def visit_ModuleNode(self, node):
- self.gv_ctx = GVContext()
+ dot_output = self.current_directives['control_flow.dot_output']
+ self.gv_ctx = GVContext() if dot_output else None
+
+ from .Optimize import ConstantFolding
self.constant_folder = ConstantFolding()
# Set of NameNode reductions
@@ -693,18 +690,15 @@
self.env = node.scope
self.stack = []
self.flow = ControlFlow()
+ self.object_expr = TypedExprNode(PyrexTypes.py_object_type, may_be_none=True)
self.visitchildren(node)
check_definitions(self.flow, self.current_directives)
- dot_output = self.current_directives['control_flow.dot_output']
if dot_output:
annotate_defs = self.current_directives['control_flow.dot_annotate_defs']
- fp = open(dot_output, 'wt')
- try:
+ with open(dot_output, 'wt') as fp:
self.gv_ctx.render(fp, 'module', annotate_defs=annotate_defs)
- finally:
- fp.close()
return node
def visit_FuncDefNode(self, node):
@@ -752,7 +746,8 @@
check_definitions(self.flow, self.current_directives)
self.flow.blocks.add(self.flow.entry_point)
- self.gv_ctx.add(GV(node.local_scope.name, self.flow))
+ if self.gv_ctx is not None:
+ self.gv_ctx.add(GV(node.local_scope.name, self.flow))
self.flow = self.stack.pop()
self.env = self.env_stack.pop()
@@ -777,19 +772,22 @@
self.flow.nextblock()
if not rhs:
- rhs = object_expr
+ rhs = self.object_expr
if lhs.is_name:
if lhs.entry is not None:
entry = lhs.entry
else:
entry = self.env.lookup(lhs.name)
- if entry is None: # TODO: This shouldn't happen...
+ if entry is None: # TODO: This shouldn't happen...
return
self.flow.mark_assignment(lhs, rhs, entry)
elif lhs.is_sequence_constructor:
for i, arg in enumerate(lhs.args):
- if not rhs or arg.is_starred:
- item_node = None
+ if arg.is_starred:
+ # "a, *b = x" assigns a list to "b"
+ item_node = TypedExprNode(Builtin.list_type, may_be_none=False, pos=arg.pos)
+ elif rhs is self.object_expr:
+ item_node = rhs
else:
item_node = rhs.inferable_item_node(i)
self.mark_assignment(arg, item_node)
@@ -814,7 +812,7 @@
return node
def visit_AssignmentNode(self, node):
- raise InternalError("Unhandled assignment node")
+ raise InternalError("Unhandled assignment node %s" % type(node))
def visit_SingleAssignmentNode(self, node):
self._visit(node.rhs)
@@ -892,6 +890,12 @@
self.mark_position(node)
return node
+ def visit_SizeofVarNode(self, node):
+ return node
+
+ def visit_TypeidNode(self, node):
+ return node
+
def visit_IfStatNode(self, node):
next_block = self.flow.newblock()
parent = self.flow.block
@@ -918,6 +922,26 @@
self.flow.block = None
return node
+ def visit_AssertStatNode(self, node):
+ """Essentially an if-condition that wraps a RaiseStatNode.
+ """
+ self.mark_position(node)
+ next_block = self.flow.newblock()
+ parent = self.flow.block
+ # failure case
+ parent = self.flow.nextblock(parent)
+ self._visit(node.condition)
+ self.flow.nextblock()
+ self._visit(node.exception)
+ if self.flow.block:
+ self.flow.block.add_child(next_block)
+ parent.add_child(next_block)
+ if next_block.parents:
+ self.flow.block = next_block
+ else:
+ self.flow.block = None
+ return node
+
def visit_WhileStatNode(self, node):
condition_block = self.flow.nextblock()
next_block = self.flow.newblock()
@@ -1015,7 +1039,7 @@
elif isinstance(node, Nodes.AsyncForStatNode):
# not entirely correct, but good enough for now
self.mark_assignment(node.target, node.item)
- else: # Parallel
+ else: # Parallel
self.mark_assignment(node.target)
# Body block
@@ -1200,7 +1224,6 @@
if self.flow.loops:
self.flow.loops[-1].exceptions.append(descr)
self.flow.block = body_block
- ## XXX: Is it still required
body_block.add_child(entry_point)
self.flow.nextblock()
self._visit(node.body)
@@ -1235,11 +1258,18 @@
self.mark_position(node)
self.visitchildren(node)
- for exception in self.flow.exceptions[::-1]:
- if exception.finally_enter:
- self.flow.block.add_child(exception.finally_enter)
- if exception.finally_exit:
- exception.finally_exit.add_child(self.flow.exit_point)
+ outer_exception_handlers = iter(self.flow.exceptions[::-1])
+ for handler in outer_exception_handlers:
+ if handler.finally_enter:
+ self.flow.block.add_child(handler.finally_enter)
+ if handler.finally_exit:
+ # 'return' goes to function exit, or to the next outer 'finally' clause
+ exit_point = self.flow.exit_point
+ for next_handler in outer_exception_handlers:
+ if next_handler.finally_enter:
+ exit_point = next_handler.finally_enter
+ break
+ handler.finally_exit.add_child(exit_point)
break
else:
if self.flow.block:
@@ -1304,10 +1334,12 @@
self.visitchildren(node, ('dict', 'metaclass',
'mkw', 'bases', 'class_result'))
self.flow.mark_assignment(node.target, node.classobj,
- self.env.lookup(node.name))
+ self.env.lookup(node.target.name))
self.env_stack.append(self.env)
self.env = node.scope
self.flow.nextblock()
+ if node.doc_node:
+ self.flow.mark_assignment(node.doc_node, fake_rhs_expr, node.doc_node.entry)
self.visitchildren(node, ('body',))
self.flow.nextblock()
self.env = self.env_stack.pop()
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/FusedNode.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/FusedNode.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/FusedNode.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/FusedNode.py 2022-03-24 10:16:46.000000000 +0000
@@ -6,6 +6,8 @@
ParseTreeTransforms, StringEncoding, Errors)
from .ExprNodes import CloneNode, ProxyNode, TupleNode
from .Nodes import FuncDefNode, CFuncDefNode, StatListNode, DefNode
+from ..Utils import OrderedSet
+from .Errors import error, CannotSpecialize
class FusedCFuncDefNode(StatListNode):
@@ -126,9 +128,6 @@
# len(permutations))
# import pprint; pprint.pprint([d for cname, d in permutations])
- if self.node.entry in env.cfunc_entries:
- env.cfunc_entries.remove(self.node.entry)
-
# Prevent copying of the python function
self.orig_py_func = orig_py_func = self.node.py_func
self.node.py_func = None
@@ -138,12 +137,33 @@
fused_types = self.node.type.get_fused_types()
self.fused_compound_types = fused_types
+ new_cfunc_entries = []
for cname, fused_to_specific in permutations:
copied_node = copy.deepcopy(self.node)
- # Make the types in our CFuncType specific
- type = copied_node.type.specialize(fused_to_specific)
+ # Make the types in our CFuncType specific.
+ try:
+ type = copied_node.type.specialize(fused_to_specific)
+ except CannotSpecialize:
+ # unlike for the argument types, specializing the return type can fail
+ error(copied_node.pos, "Return type is a fused type that cannot "
+ "be determined from the function arguments")
+ self.py_func = None # this is just to let the compiler exit gracefully
+ return
entry = copied_node.entry
+ type.specialize_entry(entry, cname)
+
+ # Reuse existing Entries (e.g. from .pxd files).
+ for i, orig_entry in enumerate(env.cfunc_entries):
+ if entry.cname == orig_entry.cname and type.same_as_resolved_type(orig_entry.type):
+ copied_node.entry = env.cfunc_entries[i]
+ if not copied_node.entry.func_cname:
+ copied_node.entry.func_cname = entry.func_cname
+ entry = copied_node.entry
+ type = entry.type
+ break
+ else:
+ new_cfunc_entries.append(entry)
copied_node.type = type
entry.type, type.entry = type, entry
@@ -164,9 +184,6 @@
self._specialize_function_args(copied_node.cfunc_declarator.args,
fused_to_specific)
- type.specialize_entry(entry, cname)
- env.cfunc_entries.append(entry)
-
# If a cpdef, declare all specialized cpdefs (this
# also calls analyse_declarations)
copied_node.declare_cpdef_wrapper(env)
@@ -180,6 +197,14 @@
if not self.replace_fused_typechecks(copied_node):
break
+ # replace old entry with new entries
+ try:
+ cindex = env.cfunc_entries.index(self.node.entry)
+ except ValueError:
+ env.cfunc_entries.extend(new_cfunc_entries)
+ else:
+ env.cfunc_entries[cindex:cindex+1] = new_cfunc_entries
+
if orig_py_func:
self.py_func = self.make_fused_cpdef(orig_py_func, env,
is_def=False)
@@ -203,12 +228,16 @@
arg.type = arg.type.specialize(fused_to_specific)
if arg.type.is_memoryviewslice:
arg.type.validate_memslice_dtype(arg.pos)
+ if arg.annotation:
+ # TODO might be nice if annotations were specialized instead?
+ # (Or might be hard to do reliably)
+ arg.annotation.untyped = True
def create_new_local_scope(self, node, env, f2s):
"""
Create a new local scope for the copied node and append it to
self.nodes. A new local scope is needed because the arguments with the
- fused types are aready in the local scope, and we need the specialized
+ fused types are already in the local scope, and we need the specialized
entries created after analyse_declarations on each specialized version
of the (CFunc)DefNode.
f2s is a dict mapping each fused type to its specialized version
@@ -247,19 +276,19 @@
Returns whether an error was issued and whether we should stop in
in order to prevent a flood of errors.
"""
- num_errors = Errors.num_errors
+ num_errors = Errors.get_errors_count()
transform = ParseTreeTransforms.ReplaceFusedTypeChecks(
copied_node.local_scope)
transform(copied_node)
- if Errors.num_errors > num_errors:
+ if Errors.get_errors_count() > num_errors:
return False
return True
def _fused_instance_checks(self, normal_types, pyx_code, env):
"""
- Genereate Cython code for instance checks, matching an object to
+ Generate Cython code for instance checks, matching an object to
specialized types.
"""
for specialized_type in normal_types:
@@ -314,19 +343,22 @@
match = "dest_sig[{{dest_sig_idx}}] = '{{specialized_type_name}}'"
no_match = "dest_sig[{{dest_sig_idx}}] = None"
- def _buffer_check_numpy_dtype(self, pyx_code, specialized_buffer_types):
+ def _buffer_check_numpy_dtype(self, pyx_code, specialized_buffer_types, pythran_types):
"""
Match a numpy dtype object to the individual specializations.
"""
self._buffer_check_numpy_dtype_setup_cases(pyx_code)
- for specialized_type in specialized_buffer_types:
+ for specialized_type in pythran_types+specialized_buffer_types:
+ final_type = specialized_type
+ if specialized_type.is_pythran_expr:
+ specialized_type = specialized_type.org_buffer
dtype = specialized_type.dtype
pyx_code.context.update(
itemsize_match=self._sizeof_dtype(dtype) + " == itemsize",
signed_match="not (%s_is_signed ^ dtype_signed)" % self._dtype_name(dtype),
dtype=dtype,
- specialized_type_name=specialized_type.specialization_string)
+ specialized_type_name=final_type.specialization_string)
dtypes = [
(dtype.is_int, pyx_code.dtype_int),
@@ -341,8 +373,11 @@
if dtype.is_int:
cond += ' and {{signed_match}}'
+ if final_type.is_pythran_expr:
+ cond += ' and arg_is_pythran_compatible'
+
if codewriter.indenter("if %s:" % cond):
- # codewriter.putln("print 'buffer match found based on numpy dtype'")
+ #codewriter.putln("print 'buffer match found based on numpy dtype'")
codewriter.putln(self.match)
codewriter.putln("break")
codewriter.dedent()
@@ -367,7 +402,7 @@
coerce_from_py_func=memslice_type.from_py_function,
dtype=dtype)
decl_code.putln(
- "{{memviewslice_cname}} {{coerce_from_py_func}}(object)")
+ "{{memviewslice_cname}} {{coerce_from_py_func}}(object, int)")
pyx_code.context.update(
specialized_type_name=specialized_type.specialization_string,
@@ -377,9 +412,9 @@
u"""
# try {{dtype}}
if itemsize == -1 or itemsize == {{sizeof_dtype}}:
- memslice = {{coerce_from_py_func}}(arg)
+ memslice = {{coerce_from_py_func}}(arg, 0)
if memslice.memview:
- __PYX_XDEC_MEMVIEW(&memslice, 1)
+ __PYX_XCLEAR_MEMVIEW(&memslice, 1)
# print 'found a match for the buffer through format parsing'
%s
break
@@ -387,7 +422,7 @@
__pyx_PyErr_Clear()
""" % self.match)
- def _buffer_checks(self, buffer_types, pyx_code, decl_code, env):
+ def _buffer_checks(self, buffer_types, pythran_types, pyx_code, decl_code, env):
"""
Generate Cython code to match objects to buffer specializations.
First try to get a numpy dtype object and match it against the individual
@@ -398,9 +433,11 @@
# The first thing to find a match in this loop breaks out of the loop
pyx_code.put_chunk(
u"""
+ """ + (u"arg_is_pythran_compatible = False" if pythran_types else u"") + u"""
if ndarray is not None:
if isinstance(arg, ndarray):
dtype = arg.dtype
+ """ + (u"arg_is_pythran_compatible = True" if pythran_types else u"") + u"""
elif __pyx_memoryview_check(arg):
arg_base = arg.base
if isinstance(arg_base, ndarray):
@@ -417,15 +454,36 @@
dtype_signed = kind == 'i'
""")
pyx_code.indent(2)
+ if pythran_types:
+ pyx_code.put_chunk(
+ u"""
+ # Pythran only supports the endianness of the current compiler
+ byteorder = dtype.byteorder
+ if byteorder == "<" and not __Pyx_Is_Little_Endian():
+ arg_is_pythran_compatible = False
+ elif byteorder == ">" and __Pyx_Is_Little_Endian():
+ arg_is_pythran_compatible = False
+ if arg_is_pythran_compatible:
+ cur_stride = itemsize
+ shape = arg.shape
+ strides = arg.strides
+ for i in range(arg.ndim-1, -1, -1):
+ if (strides[i]) != cur_stride:
+ arg_is_pythran_compatible = False
+ break
+ cur_stride *= shape[i]
+ else:
+ arg_is_pythran_compatible = not (arg.flags.f_contiguous and (arg.ndim) > 1)
+ """)
pyx_code.named_insertion_point("numpy_dtype_checks")
- self._buffer_check_numpy_dtype(pyx_code, buffer_types)
+ self._buffer_check_numpy_dtype(pyx_code, buffer_types, pythran_types)
pyx_code.dedent(2)
for specialized_type in buffer_types:
self._buffer_parse_format_string_check(
pyx_code, decl_code, specialized_type, env)
- def _buffer_declarations(self, pyx_code, decl_code, all_buffer_types):
+ def _buffer_declarations(self, pyx_code, decl_code, all_buffer_types, pythran_types):
"""
If we have any buffer specializations, write out some variable
declarations and imports.
@@ -435,7 +493,7 @@
ctypedef struct {{memviewslice_cname}}:
void *memview
- void __PYX_XDEC_MEMVIEW({{memviewslice_cname}} *, int have_gil)
+ void __PYX_XCLEAR_MEMVIEW({{memviewslice_cname}} *, int have_gil)
bint __pyx_memoryview_check(object)
""")
@@ -449,35 +507,39 @@
itemsize = -1
""")
+ if pythran_types:
+ pyx_code.local_variable_declarations.put_chunk(u"""
+ cdef bint arg_is_pythran_compatible
+ cdef Py_ssize_t cur_stride
+ """)
+
pyx_code.imports.put_chunk(
u"""
cdef type ndarray
- try:
- import numpy
- ndarray = numpy.ndarray
- except (ImportError, AttributeError, TypeError):
- ndarray = None
+ ndarray = __Pyx_ImportNumPyArrayTypeIfAvailable()
""")
+ seen_typedefs = set()
seen_int_dtypes = set()
for buffer_type in all_buffer_types:
dtype = buffer_type.dtype
+ dtype_name = self._dtype_name(dtype)
if dtype.is_typedef:
- #decl_code.putln("ctypedef %s %s" % (dtype.resolve(),
- # self._dtype_name(dtype)))
- decl_code.putln('ctypedef %s %s "%s"' % (dtype.resolve(),
- self._dtype_name(dtype),
- dtype.empty_declaration_code()))
+ if dtype_name not in seen_typedefs:
+ seen_typedefs.add(dtype_name)
+ decl_code.putln(
+ 'ctypedef %s %s "%s"' % (dtype.resolve(), dtype_name,
+ dtype.empty_declaration_code()))
if buffer_type.dtype.is_int:
if str(dtype) not in seen_int_dtypes:
seen_int_dtypes.add(str(dtype))
- pyx_code.context.update(dtype_name=self._dtype_name(dtype),
+ pyx_code.context.update(dtype_name=dtype_name,
dtype_type=self._dtype_type(dtype))
pyx_code.local_variable_declarations.put_chunk(
u"""
cdef bint {{dtype_name}}_is_signed
- {{dtype_name}}_is_signed = <{{dtype_type}}> -1 < 0
+ {{dtype_name}}_is_signed = not (<{{dtype_type}}> -1 > 0)
""")
def _split_fused_types(self, arg):
@@ -490,7 +552,7 @@
specialized_types.sort()
seen_py_type_names = set()
- normal_types, buffer_types = [], []
+ normal_types, buffer_types, pythran_types = [], [], []
has_object_fallback = False
for specialized_type in specialized_types:
py_type_name = specialized_type.py_type_name()
@@ -502,10 +564,12 @@
has_object_fallback = True
else:
normal_types.append(specialized_type)
+ elif specialized_type.is_pythran_expr:
+ pythran_types.append(specialized_type)
elif specialized_type.is_buffer or specialized_type.is_memoryviewslice:
buffer_types.append(specialized_type)
- return normal_types, buffer_types, has_object_fallback
+ return normal_types, buffer_types, pythran_types, has_object_fallback
def _unpack_argument(self, pyx_code):
pyx_code.put_chunk(
@@ -513,16 +577,41 @@
# PROCESSING ARGUMENT {{arg_tuple_idx}}
if {{arg_tuple_idx}} < len(args):
arg = (args)[{{arg_tuple_idx}}]
- elif '{{arg.name}}' in kwargs:
+ elif kwargs is not None and '{{arg.name}}' in kwargs:
arg = (kwargs)['{{arg.name}}']
else:
{{if arg.default}}
arg = (defaults)[{{default_idx}}]
{{else}}
- raise TypeError("Expected at least %d arguments" % len(args))
+ {{if arg_tuple_idx < min_positional_args}}
+ raise TypeError("Expected at least %d argument%s, got %d" % (
+ {{min_positional_args}}, {{'"s"' if min_positional_args != 1 else '""'}}, len(args)))
+ {{else}}
+ raise TypeError("Missing keyword-only argument: '%s'" % "{{arg.default}}")
+ {{endif}}
{{endif}}
""")
+ def _fused_signature_index(self, pyx_code):
+ """
+ Generate Cython code for constructing a persistent nested dictionary index of
+ fused type specialization signatures.
+ """
+ pyx_code.put_chunk(
+ u"""
+ if not _fused_sigindex:
+ for sig in signatures:
+ sigindex_node = _fused_sigindex
+ *sig_series, last_type = sig.strip('()').split('|')
+ for sig_type in sig_series:
+ if sig_type not in sigindex_node:
+ sigindex_node[sig_type] = sigindex_node = {}
+ else:
+ sigindex_node = sigindex_node[sig_type]
+ sigindex_node[last_type] = sig
+ """
+ )
+
def make_fused_cpdef(self, orig_py_func, env, is_def):
"""
This creates the function that is indexable from Python and does
@@ -539,6 +628,10 @@
'memviewslice_cname': MemoryView.memviewslice_cname,
'func_args': self.node.args,
'n_fused': len(fused_types),
+ 'min_positional_args':
+ self.node.num_required_args - self.node.num_required_kw_args
+ if is_def else
+ sum(1 for arg in self.node.args if arg.default is None),
'name': orig_py_func.entry.name,
}
@@ -548,35 +641,39 @@
u"""
cdef extern from *:
void __pyx_PyErr_Clear "PyErr_Clear" ()
+ type __Pyx_ImportNumPyArrayTypeIfAvailable()
+ int __Pyx_Is_Little_Endian()
""")
decl_code.indent()
pyx_code.put_chunk(
u"""
- from __future__ import absolute_import # for later numpy import
- """)
- pyx_code.put_chunk(
- u"""
- def __pyx_fused_cpdef(signatures, args, kwargs, defaults):
+ def __pyx_fused_cpdef(signatures, args, kwargs, defaults, _fused_sigindex={}):
# FIXME: use a typed signature - currently fails badly because
# default arguments inherit the types we specify here!
+ cdef list search_list
+
+ cdef dict sn, sigindex_node
+
dest_sig = [None] * {{n_fused}}
- if kwargs is None:
- kwargs = {}
+ if kwargs is not None and not kwargs:
+ kwargs = None
cdef Py_ssize_t i
# instance check body
""")
- pyx_code.indent() # indent following code to function body
+
+ pyx_code.indent() # indent following code to function body
pyx_code.named_insertion_point("imports")
+ pyx_code.named_insertion_point("func_defs")
pyx_code.named_insertion_point("local_variable_declarations")
fused_index = 0
default_idx = 0
- all_buffer_types = set()
+ all_buffer_types = OrderedSet()
seen_fused_types = set()
for i, arg in enumerate(self.node.args):
if arg.type.is_fused:
@@ -596,15 +693,16 @@
default_idx=default_idx,
)
- normal_types, buffer_types, has_object_fallback = self._split_fused_types(arg)
+ normal_types, buffer_types, pythran_types, has_object_fallback = self._split_fused_types(arg)
self._unpack_argument(pyx_code)
# 'unrolled' loop, first match breaks out of it
if pyx_code.indenter("while 1:"):
if normal_types:
self._fused_instance_checks(normal_types, pyx_code, env)
- if buffer_types:
- self._buffer_checks(buffer_types, pyx_code, decl_code, env)
+ if buffer_types or pythran_types:
+ env.use_utility_code(Code.UtilityCode.load_cached("IsLittleEndian", "ModuleSetupCode.c"))
+ self._buffer_checks(buffer_types, pythran_types, pyx_code, decl_code, env)
if has_object_fallback:
pyx_code.context.update(specialized_type_name='object')
pyx_code.putln(self.match)
@@ -615,29 +713,46 @@
fused_index += 1
all_buffer_types.update(buffer_types)
+ all_buffer_types.update(ty.org_buffer for ty in pythran_types)
if arg.default:
default_idx += 1
if all_buffer_types:
- self._buffer_declarations(pyx_code, decl_code, all_buffer_types)
+ self._buffer_declarations(pyx_code, decl_code, all_buffer_types, pythran_types)
env.use_utility_code(Code.UtilityCode.load_cached("Import", "ImportExport.c"))
+ env.use_utility_code(Code.UtilityCode.load_cached("ImportNumPyArray", "ImportExport.c"))
+
+ self._fused_signature_index(pyx_code)
pyx_code.put_chunk(
u"""
- candidates = []
- for sig in signatures:
- match_found = False
- for src_type, dst_type in zip(sig.strip('()').split('|'), dest_sig):
- if dst_type is not None:
- if src_type == dst_type:
- match_found = True
- else:
- match_found = False
- break
+ sigindex_matches = []
+ sigindex_candidates = [_fused_sigindex]
- if match_found:
- candidates.append(sig)
+ for dst_type in dest_sig:
+ found_matches = []
+ found_candidates = []
+ # Make two seperate lists: One for signature sub-trees
+ # with at least one definite match, and another for
+ # signature sub-trees with only ambiguous matches
+ # (where `dest_sig[i] is None`).
+ if dst_type is None:
+ for sn in sigindex_matches:
+ found_matches.extend(sn.values())
+ for sn in sigindex_candidates:
+ found_candidates.extend(sn.values())
+ else:
+ for search_list in (sigindex_matches, sigindex_candidates):
+ for sn in search_list:
+ if dst_type in sn:
+ found_matches.append(sn[dst_type])
+ sigindex_matches = found_matches
+ sigindex_candidates = found_candidates
+ if not (found_matches or found_candidates):
+ break
+
+ candidates = sigindex_matches
if not candidates:
raise TypeError("No matching signature found")
@@ -726,16 +841,18 @@
for arg in self.node.args:
if arg.default:
arg.default = arg.default.analyse_expressions(env)
- defaults.append(ProxyNode(arg.default))
+ # coerce the argument to temp since CloneNode really requires a temp
+ defaults.append(ProxyNode(arg.default.coerce_to_temp(env)))
else:
defaults.append(None)
for i, stat in enumerate(self.stats):
stat = self.stats[i] = stat.analyse_expressions(env)
- if isinstance(stat, FuncDefNode):
+ if isinstance(stat, FuncDefNode) and stat is not self.py_func:
+ # the dispatcher specifically doesn't want its defaults overriding
for arg, default in zip(stat.args, defaults):
if default is not None:
- arg.default = CloneNode(default).coerce_to(arg.type, env)
+ arg.default = CloneNode(default).analyse_expressions(env).coerce_to(arg.type, env)
if self.py_func:
args = [CloneNode(default) for default in defaults if default]
@@ -763,6 +880,10 @@
else:
nodes = self.nodes
+ # For the moment, fused functions do not support METH_FASTCALL
+ for node in nodes:
+ node.entry.signature.use_fastcall = False
+
signatures = [StringEncoding.EncodedString(node.specialized_signature_string)
for node in nodes]
keys = [ExprNodes.StringNode(node.pos, value=sig)
@@ -811,18 +932,24 @@
"((__pyx_FusedFunctionObject *) %s)->__signatures__ = %s;" %
(self.resulting_fused_function.result(),
self.__signatures__.result()))
- code.put_giveref(self.__signatures__.result())
+ self.__signatures__.generate_giveref(code)
+ self.__signatures__.generate_post_assignment_code(code)
+ self.__signatures__.free_temps(code)
self.fused_func_assignment.generate_execution_code(code)
# Dispose of results
self.resulting_fused_function.generate_disposal_code(code)
+ self.resulting_fused_function.free_temps(code)
self.defaults_tuple.generate_disposal_code(code)
+ self.defaults_tuple.free_temps(code)
self.code_object.generate_disposal_code(code)
+ self.code_object.free_temps(code)
for default in self.defaults:
if default is not None:
default.generate_disposal_code(code)
+ default.free_temps(code)
def annotate(self, code):
for stat in self.stats:
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Future.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Future.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Future.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Future.py 2022-03-24 10:16:46.000000000 +0000
@@ -4,12 +4,13 @@
return getattr(__future__, name, object())
unicode_literals = _get_feature("unicode_literals")
-with_statement = _get_feature("with_statement")
+with_statement = _get_feature("with_statement") # dummy
division = _get_feature("division")
print_function = _get_feature("print_function")
absolute_import = _get_feature("absolute_import")
nested_scopes = _get_feature("nested_scopes") # dummy
generators = _get_feature("generators") # dummy
generator_stop = _get_feature("generator_stop")
+annotations = _get_feature("annotations")
del _get_feature
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Interpreter.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Interpreter.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Interpreter.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Interpreter.py 2022-03-24 10:16:46.000000000 +0000
@@ -47,8 +47,8 @@
raise CompileError(node.pos, "Type not allowed here.")
else:
if (sys.version_info[0] >=3 and
- isinstance(node, StringNode) and
- node.unicode_value is not None):
+ isinstance(node, StringNode) and
+ node.unicode_value is not None):
return (node.unicode_value, node.pos)
return (node.compile_time_value(empty_scope), node.pos)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Lexicon.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Lexicon.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Lexicon.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Lexicon.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,9 +1,10 @@
+# -*- coding: utf-8 -*-
# cython: language_level=3, py2_import=True
#
# Cython Scanner - Lexical Definitions
#
-from __future__ import absolute_import
+from __future__ import absolute_import, unicode_literals
raw_prefixes = "rR"
bytes_prefixes = "bB"
@@ -16,28 +17,43 @@
def make_lexicon():
from ..Plex import \
Str, Any, AnyBut, AnyChar, Rep, Rep1, Opt, Bol, Eol, Eof, \
- TEXT, IGNORE, State, Lexicon
- from .Scanning import Method
+ TEXT, IGNORE, Method, State, Lexicon, Range
- letter = Any("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")
+ nonzero_digit = Any("123456789")
digit = Any("0123456789")
bindigit = Any("01")
octdigit = Any("01234567")
hexdigit = Any("0123456789ABCDEFabcdef")
indentation = Bol + Rep(Any(" \t"))
+ # The list of valid unicode identifier characters are pretty slow to generate at runtime,
+ # and require Python3, so are just included directly here
+ # (via the generated code block at the bottom of the file)
+ unicode_start_character = (Any(unicode_start_ch_any) | Range(unicode_start_ch_range))
+ unicode_continuation_character = (
+ unicode_start_character |
+ Any(unicode_continuation_ch_any) | Range(unicode_continuation_ch_range))
+
def underscore_digits(d):
return Rep1(d) + Rep(Str("_") + Rep1(d))
+ def prefixed_digits(prefix, digits):
+ return prefix + Opt(Str("_")) + underscore_digits(digits)
+
decimal = underscore_digits(digit)
dot = Str(".")
exponent = Any("Ee") + Opt(Any("+-")) + decimal
decimal_fract = (decimal + dot + Opt(decimal)) | (dot + decimal)
- name = letter + Rep(letter | digit)
- intconst = decimal | (Str("0") + ((Any("Xx") + underscore_digits(hexdigit)) |
- (Any("Oo") + underscore_digits(octdigit)) |
- (Any("Bb") + underscore_digits(bindigit)) ))
+ #name = letter + Rep(letter | digit)
+ name = unicode_start_character + Rep(unicode_continuation_character)
+ intconst = (prefixed_digits(nonzero_digit, digit) | # decimal literals with underscores must not start with '0'
+ (Str("0") + (prefixed_digits(Any("Xx"), hexdigit) |
+ prefixed_digits(Any("Oo"), octdigit) |
+ prefixed_digits(Any("Bb"), bindigit) )) |
+ underscore_digits(Str('0')) # 0_0_0_0... is allowed as a decimal literal
+ | Rep1(digit) # FIXME: remove these Py2 style decimal/octal literals (PY_VERSION_HEX < 3)
+ )
intsuffix = (Opt(Any("Uu")) + Opt(Any("Ll")) + Opt(Any("Ll"))) | (Opt(Any("Ll")) + Opt(Any("Ll")) + Opt(Any("Uu")))
intliteral = intconst + intsuffix
fltconst = (decimal_fract + Opt(exponent)) | (decimal + exponent)
@@ -61,7 +77,7 @@
punct = Any(":,;+-*/|&<>=.%`~^?!@")
diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**", "//",
"+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=",
- "<<=", ">>=", "**=", "//=", "->", "@=")
+ "<<=", ">>=", "**=", "//=", "->", "@=", "&&", "||", ':=')
spaces = Rep1(Any(" \t\f"))
escaped_newline = Str("\\\n")
lineterm = Eol + Opt(Str("\n"))
@@ -69,7 +85,7 @@
comment = Str("#") + Rep(AnyBut("\n"))
return Lexicon([
- (name, IDENT),
+ (name, Method('normalize_ident')),
(intliteral, Method('strip_underscores', symbol='INT')),
(fltconst, Method('strip_underscores', symbol='FLOAT')),
(imagconst, Method('strip_underscores', symbol='IMAG')),
@@ -136,3 +152,50 @@
#debug_file = scanner_dump_file
)
+
+# BEGIN GENERATED CODE
+# generated with:
+# cpython 3.10.0a0 (heads/master:2b0e654f91, May 29 2020, 16:17:52)
+
+unicode_start_ch_any = (
+ u"_ªµºˬˮͿΆΌՙەۿܐޱߺࠚࠤࠨऽॐলঽৎৼਫ਼ઽૐૹଽୱஃஜௐఽಀಽೞഽൎලาຄລາຽໆༀဿၡႎჇჍቘዀៗៜᢪᪧᳺὙ"
+ u"ὛὝιⁱⁿℂℇℕℤΩℨⅎⴧⴭⵯꣻꧏꩺꪱꫀꫂיִמּﹱﹳﹷﹹﹻﹽ𐠈𐠼𐨀𐼧𑅄𑅇𑅶𑇚𑇜𑊈𑌽𑍐𑓇𑙄𑚸𑤉𑤿𑥁𑧡𑧣𑨀𑨺𑩐𑪝𑱀𑵆𑶘𑾰𖽐𖿣𝒢"
+ u"𝒻𝕆𞅎𞥋𞸤𞸧𞸹𞸻𞹂𞹇𞹉𞹋𞹔𞹗𞹙𞹛𞹝𞹟𞹤𞹾"
+)
+unicode_start_ch_range = (
+ u"AZazÀÖØöøˁˆˑˠˤͰʹͶͷͻͽΈΊΎΡΣϵϷҁҊԯԱՖՠֈאתׯײؠيٮٯٱۓۥۦۮۯۺۼܒܯݍޥߊߪߴߵࠀࠕ"
+ u"ࡀࡘࡠࡪࢠࢴࢶࣇऄहक़ॡॱঀঅঌএঐওনপরশহড়ঢ়য়ৡৰৱਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹਖ਼ੜੲੴઅઍએઑઓનપરલળવહ"
+ u"ૠૡଅଌଏଐଓନପରଲଳଵହଡ଼ଢ଼ୟୡஅஊஎஐஒகஙசஞடணதநபமஹఅఌఎఐఒనపహౘౚౠౡಅಌಎಐಒನಪಳವಹೠೡೱೲ"
+ u"ഄഌഎഐഒഺൔൖൟൡൺൿඅඖකනඳරවෆกะเๆກຂຆຊຌຣວະເໄໜໟཀཇཉཬྈྌကဪၐၕၚၝၥၦၮၰၵႁႠჅაჺჼቈ"
+ u"ቊቍቐቖቚቝበኈኊኍነኰኲኵኸኾዂዅወዖዘጐጒጕጘፚᎀᎏᎠᏵᏸᏽᐁᙬᙯᙿᚁᚚᚠᛪᛮᛸᜀᜌᜎᜑᜠᜱᝀᝑᝠᝬᝮᝰកឳᠠᡸᢀᢨ"
+ u"ᢰᣵᤀᤞᥐᥭᥰᥴᦀᦫᦰᧉᨀᨖᨠᩔᬅᬳᭅᭋᮃᮠᮮᮯᮺᯥᰀᰣᱍᱏᱚᱽᲀᲈᲐᲺᲽᲿᳩᳬᳮᳳᳵᳶᴀᶿḀἕἘἝἠὅὈὍὐὗὟώᾀᾴ"
+ u"ᾶᾼῂῄῆῌῐΐῖΊῠῬῲῴῶῼₐₜℊℓ℘ℝKℹℼℿⅅⅉⅠↈⰀⰮⰰⱞⱠⳤⳫⳮⳲⳳⴀⴥⴰⵧⶀⶖⶠⶦⶨⶮⶰⶶⶸⶾⷀⷆⷈⷎⷐⷖ"
+ u"ⷘⷞ々〇〡〩〱〵〸〼ぁゖゝゟァヺーヿㄅㄯㄱㆎㆠㆿㇰㇿ㐀䶿一鿼ꀀꒌꓐꓽꔀꘌꘐꘟꘪꘫꙀꙮꙿꚝꚠꛯꜗꜟꜢꞈꞋꞿꟂꟊꟵꠁꠃꠅꠇꠊ"
+ u"ꠌꠢꡀꡳꢂꢳꣲꣷꣽꣾꤊꤥꤰꥆꥠꥼꦄꦲꧠꧤꧦꧯꧺꧾꨀꨨꩀꩂꩄꩋꩠꩶꩾꪯꪵꪶꪹꪽꫛꫝꫠꫪꫲꫴꬁꬆꬉꬎꬑꬖꬠꬦꬨꬮꬰꭚꭜꭩꭰꯢ"
+ u"가힣ힰퟆퟋퟻ豈舘並龎ffstﬓﬗײַﬨשׁזּטּלּנּסּףּפּצּﮱﯓﱝﱤﴽﵐﶏﶒﷇﷰﷹﹿﻼAZazヲンᅠ하ᅦᅧᅬᅭᅲᅳᅵ𐀀𐀋𐀍𐀦𐀨𐀺"
+ u"𐀼𐀽𐀿𐁍𐁐𐁝𐂀𐃺𐅀𐅴𐊀𐊜𐊠𐋐𐌀𐌟𐌭𐍊𐍐𐍵𐎀𐎝𐎠𐏃𐏈𐏏𐏑𐏕𐐀𐒝𐒰𐓓𐓘𐓻𐔀𐔧𐔰𐕣𐘀𐜶𐝀𐝕𐝠𐝧𐠀𐠅𐠊𐠵𐠷𐠸𐠿𐡕𐡠𐡶𐢀𐢞𐣠𐣲𐣴𐣵"
+ u"𐤀𐤕𐤠𐤹𐦀𐦷𐦾𐦿𐨐𐨓𐨕𐨗𐨙𐨵𐩠𐩼𐪀𐪜𐫀𐫇𐫉𐫤𐬀𐬵𐭀𐭕𐭠𐭲𐮀𐮑𐰀𐱈𐲀𐲲𐳀𐳲𐴀𐴣𐺀𐺩𐺰𐺱𐼀𐼜𐼰𐽅𐾰𐿄𐿠𐿶𑀃𑀷𑂃𑂯𑃐𑃨𑄃𑄦𑅐𑅲"
+ u"𑆃𑆲𑇁𑇄𑈀𑈑𑈓𑈫𑊀𑊆𑊊𑊍𑊏𑊝𑊟𑊨𑊰𑋞𑌅𑌌𑌏𑌐𑌓𑌨𑌪𑌰𑌲𑌳𑌵𑌹𑍝𑍡𑐀𑐴𑑇𑑊𑑟𑑡𑒀𑒯𑓄𑓅𑖀𑖮𑗘𑗛𑘀𑘯𑚀𑚪𑜀𑜚𑠀𑠫𑢠𑣟𑣿𑤆𑤌𑤓"
+ u"𑤕𑤖𑤘𑤯𑦠𑦧𑦪𑧐𑨋𑨲𑩜𑪉𑫀𑫸𑰀𑰈𑰊𑰮𑱲𑲏𑴀𑴆𑴈𑴉𑴋𑴰𑵠𑵥𑵧𑵨𑵪𑶉𑻠𑻲𒀀𒎙𒐀𒑮𒒀𒕃𓀀𓐮𔐀𔙆𖠀𖨸𖩀𖩞𖫐𖫭𖬀𖬯𖭀𖭃𖭣𖭷𖭽𖮏𖹀𖹿"
+ u"𖼀𖽊𖾓𖾟𖿠𖿡𗀀𘟷𘠀𘳕𘴀𘴈𛀀𛄞𛅐𛅒𛅤𛅧𛅰𛋻𛰀𛱪𛱰𛱼𛲀𛲈𛲐𛲙𝐀𝑔𝑖𝒜𝒞𝒟𝒥𝒦𝒩𝒬𝒮𝒹𝒽𝓃𝓅𝔅𝔇𝔊𝔍𝔔𝔖𝔜𝔞𝔹𝔻𝔾𝕀𝕄𝕊𝕐𝕒𝚥"
+ u"𝚨𝛀𝛂𝛚𝛜𝛺𝛼𝜔𝜖𝜴𝜶𝝎𝝐𝝮𝝰𝞈𝞊𝞨𝞪𝟂𝟄𝟋𞄀𞄬𞄷𞄽𞋀𞋫𞠀𞣄𞤀𞥃𞸀𞸃𞸅𞸟𞸡𞸢𞸩𞸲𞸴𞸷𞹍𞹏𞹑𞹒𞹡𞹢𞹧𞹪𞹬𞹲𞹴𞹷𞹹𞹼𞺀𞺉𞺋𞺛"
+ u"𞺡𞺣𞺥𞺩𞺫𞺻𠀀𪛝𪜀𫜴𫝀𫠝𫠠𬺡𬺰𮯠丽𪘀"
+)
+unicode_continuation_ch_any = (
+ u"··়ׇֿٰܑ߽ৗ਼৾ੑੵ઼଼ஂௗ಼ൗ්ූัັ༹༵༷࿆᳭ᢩ៝᳴⁔⵿⃡꙯ꠂ꠆ꠋ꠬ꧥꩃﬞꪰ꫁_𑅳𐨿𐇽𐋠𑈾𑍗𑑞𑥀𑧤𑩇𑴺𑵇𖽏𖿤𝩵"
+ u"𝪄"
+)
+unicode_continuation_ch_range = (
+ u"09ֽׁׂًؚ֑ׅ̀ͯ҃҇ׄؐ٩۪ۭۖۜ۟ۤۧۨ۰۹ܰ݊ަް߀߉࡙࡛࣓ࣣ߫߳ࠖ࠙ࠛࠣࠥࠧࠩ࠭࣡ःऺ़ाॏ॑ॗॢॣ०९ঁঃ"
+ u"াৄেৈো্ৢৣ০৯ਁਃਾੂੇੈੋ੍੦ੱઁઃાૅેૉો્ૢૣ૦૯ૺ૿ଁଃାୄେୈୋ୍୕ୗୢୣ୦୯ாூெைொ்௦௯ఀఄాౄ"
+ u"ెైొ్ౕౖౢౣ౦౯ಁಃಾೄೆೈೊ್ೕೖೢೣ೦೯ഀഃ഻഼ാൄെൈൊ്ൢൣ൦൯ඁඃාුෘෟ෦෯ෲෳำฺ็๎๐๙ຳຼ່ໍ໐໙"
+ u"༘༙༠༩༾༿྄ཱ྆྇ྍྗྙྼါှ၀၉ၖၙၞၠၢၤၧၭၱၴႂႍႏႝ፝፟፩፱ᜒ᜔ᜲ᜴ᝒᝓᝲᝳ឴៓០៩᠋᠍᠐᠙ᤠᤫᤰ᤻᥆᥏᧐᧚"
+ u"ᨗᨛᩕᩞ᩠᩿᩼᪉᪐᪙᪽ᪿᫀ᪰ᬀᬄ᬴᭄᭐᭙᭫᭳ᮀᮂᮡᮭ᮰᮹᯦᯳ᰤ᰷᱀᱉᱐᱙᳔᳨᳐᳒᳷᷹᷿᳹᷀᷻‿⁀⃥゙゚〪〯⃐⃜⃰⳯⳱ⷠⷿ"
+ u"꘠꘩ꙴ꙽ꚞꚟ꛰꛱ꠣꠧꢀꢁꢴꣅ꣐꣙꣠꣱ꣿ꤉ꤦ꤭ꥇ꥓ꦀꦃ꦳꧀꧐꧙꧰꧹ꨩꨶꩌꩍ꩐꩙ꩻꩽꪴꪲꪷꪸꪾ꪿ꫫꫯꫵ꫶ꯣꯪ꯬꯭꯰꯹︀️︠︯"
+ u"︳︴﹍﹏09゙゚𐍶𐍺𐒠𐒩𐨁𐨃𐨅𐨆𐨌𐨺𐫦𐨏𐨸𐫥𐴤𐴧𐴰𐴹𐽆𐽐𐺫𐺬𑀀𑀂𑀸𑁆𑁦𑁯𑁿𑂂𑂰𑂺𑃰𑃹𑄀𑄂𑄧𑄴𑄶𑄿𑅅𑅆𑆀𑆂𑆳𑇀𑇉𑇌𑇎𑇙𑈬𑈷"
+ u"𑋟𑋪𑋰𑋹𑌀𑌃𑌻𑌼𑌾𑍄𑍇𑍈𑍋𑍍𑍢𑍣𑍦𑍬𑍰𑍴𑐵𑑆𑑐𑑙𑒰𑓃𑓐𑓙𑖯𑖵𑖸𑗀𑗜𑗝𑘰𑙀𑙐𑙙𑚫𑚷𑛀𑛉𑜝𑜫𑜰𑜹𑠬𑠺𑣠𑣩𑤰𑤵𑤷𑤸𑤻𑤾𑥂𑥃𑥐𑥙"
+ u"𑧑𑧗𑧚𑧠𑨁𑨊𑨳𑨹𑨻𑨾𑩑𑩛𑪊𑪙𑰯𑰶𑰸𑰿𑱐𑱙𑲒𑲧𑲩𑲶𑴱𑴶𑴼𑴽𑴿𑵅𑵐𑵙𑶊𑶎𑶐𑶑𑶓𑶗𑶠𑶩𑻳𑻶𖩠𖩩𖫰𖫴𖬰𖬶𖭐𖭙𖽑𖾇𖾏𖾒𖿰𖿱𛲝𛲞𝅩𝅥"
+ u"𝅲𝅻𝆂𝆋𝅭𝆅𝆪𝆭𝉂𝉄𝟎𝟿𝨀𝨶𝨻𝩬𝪛𝪟𝪡𝪯𞀀𞀆𞀈𞀘𞀛𞀡𞀣𞀤𞀦𞀪𞄰𞄶𞅀𞅉𞋬𞋹𞥊𞣐𞣖𞥄𞥐𞥙🯰🯹"
+)
+
+# END GENERATED CODE
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Compiler/Main.py cython-0.20.1+1~202203241016-9537/Cython/Compiler/Main.py
--- cython-0.20.1+1~201611251650-6686/Cython/Compiler/Main.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Compiler/Main.py 2022-03-24 10:16:46.000000000 +0000
@@ -9,8 +9,8 @@
import sys
import io
-if sys.version_info[:2] < (2, 6) or (3, 0) <= sys.version_info[:2] < (3, 2):
- sys.stderr.write("Sorry, Cython requires Python 2.6+ or 3.2+, found %d.%d\n" % tuple(sys.version_info[:2]))
+if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 3):
+ sys.stderr.write("Sorry, Cython requires Python 2.7 or 3.3+, found %d.%d\n" % tuple(sys.version_info[:2]))
sys.exit(1)
try:
@@ -18,39 +18,41 @@
except ImportError:
basestring = str
-from . import Errors
# Do not import Parsing here, import it when needed, because Parsing imports
# Nodes, which globally needs debug command line options initialized to set a
# conditional metaclass. These options are processed by CmdLine called from
# main() in this file.
# import Parsing
+from . import Errors
from .StringEncoding import EncodedString
from .Scanning import PyrexScanner, FileSourceDescriptor
from .Errors import PyrexError, CompileError, error, warning
from .Symtab import ModuleScope
from .. import Utils
from . import Options
+from .Options import CompilationOptions, default_options
+from .CmdLine import parse_command_line
+from .Lexicon import (unicode_start_ch_any, unicode_continuation_ch_any,
+ unicode_start_ch_range, unicode_continuation_ch_range)
+
+
+def _make_range_re(chrs):
+ out = []
+ for i in range(0, len(chrs), 2):
+ out.append(u"{0}-{1}".format(chrs[i], chrs[i+1]))
+ return u"".join(out)
+
+# py2 version looked like r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$"
+module_name_pattern = u"[{0}{1}][{0}{2}{1}{3}]*".format(
+ unicode_start_ch_any, _make_range_re(unicode_start_ch_range),
+ unicode_continuation_ch_any,
+ _make_range_re(unicode_continuation_ch_range))
+module_name_pattern = re.compile(u"{0}(\\.{0})*$".format(module_name_pattern))
-from . import Version # legacy import needed by old PyTables versions
-version = Version.version # legacy attribute - use "Cython.__version__" instead
-module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
+standard_include_path = os.path.abspath(
+ os.path.join(os.path.dirname(os.path.dirname(__file__)), 'Includes'))
-verbose = 0
-
-class CompilationData(object):
- # Bundles the information that is passed from transform to transform.
- # (For now, this is only)
-
- # While Context contains every pxd ever loaded, path information etc.,
- # this only contains the data related to a single compilation pass
- #
- # pyx ModuleNode Main code tree of this compilation.
- # pxds {string : ModuleNode} Trees for the pxds used in the pyx.
- # codewriter CCodeWriter Where to output final code.
- # options CompilationOptions
- # result CompilationResult
- pass
class Context(object):
# This class encapsulates the context needed for compiling
@@ -65,9 +67,10 @@
# language_level int currently 2 or 3 for Python 2/3
cython_scope = None
+ language_level = None # warn when not set but default to Py2
def __init__(self, include_directories, compiler_directives, cpp=False,
- language_level=2, options=None, create_testscope=True):
+ language_level=None, options=None):
# cython_scope is a hack, set to False by subclasses, in order to break
# an infinite loop.
# Better code organization would fix it.
@@ -85,19 +88,30 @@
self.pxds = {} # full name -> node tree
self._interned = {} # (type(value), value, *key_args) -> interned_value
- standard_include_path = os.path.abspath(os.path.normpath(
- os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
- self.include_directories = include_directories + [standard_include_path]
-
- self.set_language_level(language_level)
+ if language_level is not None:
+ self.set_language_level(language_level)
self.gdb_debug_outputwriter = None
+ @classmethod
+ def from_options(cls, options):
+ return cls(options.include_path, options.compiler_directives,
+ options.cplus, options.language_level, options=options)
+
def set_language_level(self, level):
+ from .Future import print_function, unicode_literals, absolute_import, division, generator_stop
+ future_directives = set()
+ if level == '3str':
+ level = 3
+ else:
+ level = int(level)
+ if level >= 3:
+ future_directives.add(unicode_literals)
+ if level >= 3:
+ future_directives.update([print_function, absolute_import, division, generator_stop])
self.language_level = level
+ self.future_directives = future_directives
if level >= 3:
- from .Future import print_function, unicode_literals, absolute_import, division
- self.future_directives.update([print_function, unicode_literals, absolute_import, division])
self.modules['builtins'] = self.modules['__builtin__']
def intern_ustring(self, value, encoding=None):
@@ -112,15 +126,6 @@
self._interned[key] = value
return value
- def intern_value(self, value, *key):
- key = (type(value), value) + key
- try:
- return self._interned[key]
- except KeyError:
- pass
- self._interned[key] = value
- return value
-
# pipeline creation functions can now be found in Pipeline.py
def process_pxd(self, source_desc, scope, module_name):
@@ -168,7 +173,7 @@
if not module_name_pattern.match(qualified_name):
raise CompileError(pos or (module_name, 0, 0),
- "'%s' is not a valid module name" % module_name)
+ u"'%s' is not a valid module name" % module_name)
if relative_to:
if debug_find_module:
@@ -204,8 +209,9 @@
# Set pxd_file_loaded such that we don't need to
# look for the non-existing pxd file next time.
scope.pxd_file_loaded = True
- package_pathname = self.search_include_directories(qualified_name, ".py", pos)
- if package_pathname and package_pathname.endswith('__init__.py'):
+ package_pathname = self.search_include_directories(
+ qualified_name, suffix=".py", source_pos=pos)
+ if package_pathname and package_pathname.endswith(Utils.PACKAGE_FILES):
pass
else:
error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep))
@@ -227,7 +233,7 @@
pass
return scope
- def find_pxd_file(self, qualified_name, pos, sys_path=True):
+ def find_pxd_file(self, qualified_name, pos=None, sys_path=True, source_file_path=None):
# Search include path (and sys.path if sys_path is True) for
# the .pxd file corresponding to the given fully-qualified
# module name.
@@ -236,48 +242,36 @@
# the directory containing the source file is searched first
# for a dotted filename, and its containing package root
# directory is searched first for a non-dotted filename.
- pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=sys_path)
- if pxd is None: # XXX Keep this until Includes/Deprecated is removed
- if (qualified_name.startswith('python') or
- qualified_name in ('stdlib', 'stdio', 'stl')):
- standard_include_path = os.path.abspath(os.path.normpath(
- os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
- deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
- self.include_directories.append(deprecated_include_path)
- try:
- pxd = self.search_include_directories(qualified_name, ".pxd", pos)
- finally:
- self.include_directories.pop()
- if pxd:
- name = qualified_name
- if name.startswith('python'):
- warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
- elif name in ('stdlib', 'stdio'):
- warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
- elif name in ('stl'):
- warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
+ pxd = self.search_include_directories(
+ qualified_name, suffix=".pxd", source_pos=pos, sys_path=sys_path, source_file_path=source_file_path)
if pxd is None and Options.cimport_from_pyx:
return self.find_pyx_file(qualified_name, pos)
return pxd
- def find_pyx_file(self, qualified_name, pos):
+ def find_pyx_file(self, qualified_name, pos=None, source_file_path=None):
# Search include path for the .pyx file corresponding to the
# given fully-qualified module name, as for find_pxd_file().
- return self.search_include_directories(qualified_name, ".pyx", pos)
+ return self.search_include_directories(
+ qualified_name, suffix=".pyx", source_pos=pos, source_file_path=source_file_path)
- def find_include_file(self, filename, pos):
+ def find_include_file(self, filename, pos=None, source_file_path=None):
# Search list of include directories for filename.
# Reports an error and returns None if not found.
- path = self.search_include_directories(filename, "", pos,
- include=True)
+ path = self.search_include_directories(
+ filename, source_pos=pos, include=True, source_file_path=source_file_path)
if not path:
error(pos, "'%s' not found" % filename)
return path
- def search_include_directories(self, qualified_name, suffix, pos,
- include=False, sys_path=False):
- return Utils.search_include_directories(
- tuple(self.include_directories), qualified_name, suffix, pos, include, sys_path)
+ def search_include_directories(self, qualified_name,
+ suffix=None, source_pos=None, include=False, sys_path=False, source_file_path=None):
+ include_dirs = self.include_directories
+ if sys_path:
+ include_dirs = include_dirs + sys.path
+ # include_dirs must be hashable for caching in @cached_function
+ include_dirs = tuple(include_dirs + [standard_include_path])
+ return search_include_directories(
+ include_dirs, qualified_name, suffix or "", source_pos, include, source_file_path)
def find_root_package_dir(self, file_path):
return Utils.find_root_package_dir(file_path)
@@ -291,15 +285,14 @@
c_time = Utils.modification_time(output_path)
if Utils.file_newer_than(source_path, c_time):
return 1
- pos = [source_path]
pxd_path = Utils.replace_suffix(source_path, ".pxd")
if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
return 1
for kind, name in self.read_dependency_file(source_path):
if kind == "cimport":
- dep_path = self.find_pxd_file(name, pos)
+ dep_path = self.find_pxd_file(name, source_file_path=source_path)
elif kind == "include":
- dep_path = self.search_include_directories(name, pos)
+ dep_path = self.search_include_directories(name, source_file_path=source_path)
else:
continue
if dep_path and Utils.file_newer_than(dep_path, c_time):
@@ -344,7 +337,7 @@
source_filename = source_desc.filename
scope.cpp = self.cpp
# Parse the given source file and return a parse tree.
- num_errors = Errors.num_errors
+ num_errors = Errors.get_errors_count()
try:
with Utils.open_source_file(source_filename) as f:
from . import Parsing
@@ -356,14 +349,14 @@
from ..Parser import ConcreteSyntaxTree
except ImportError:
raise RuntimeError(
- "Formal grammer can only be used with compiled Cython with an available pgen.")
+ "Formal grammar can only be used with compiled Cython with an available pgen.")
ConcreteSyntaxTree.p_module(source_filename)
except UnicodeDecodeError as e:
#import traceback
#traceback.print_exc()
raise self._report_decode_error(source_desc, e)
- if Errors.num_errors > num_errors:
+ if Errors.get_errors_count() > num_errors:
raise CompileError()
return tree
@@ -403,20 +396,19 @@
return ".".join(names)
def setup_errors(self, options, result):
- Errors.reset() # clear any remaining error state
+ Errors.init_thread()
if options.use_listing_file:
path = result.listing_file = Utils.replace_suffix(result.main_source_file, ".lis")
else:
path = None
- Errors.open_listing_file(path=path,
- echo_to_stderr=options.errors_to_stderr)
+ Errors.open_listing_file(path=path, echo_to_stderr=options.errors_to_stderr)
def teardown_errors(self, err, options, result):
source_desc = result.compilation_source.source_desc
if not isinstance(source_desc, FileSourceDescriptor):
raise RuntimeError("Only file sources for code supported")
Errors.close_listing_file()
- result.num_errors = Errors.num_errors
+ result.num_errors = Errors.get_errors_count()
if result.num_errors > 0:
err = True
if err and result.c_file:
@@ -426,6 +418,7 @@
pass
result.c_file = None
+
def get_output_filename(source_filename, cwd, options):
if options.cplus:
c_suffix = ".cpp"
@@ -441,6 +434,7 @@
else:
return suggested_file_name
+
def create_default_resultobj(compilation_source, options):
result = CompilationResult()
result.main_source_file = compilation_source.source_desc.filename
@@ -451,23 +445,33 @@
result.embedded_metadata = options.embedded_metadata
return result
+
def run_pipeline(source, options, full_module_name=None, context=None):
from . import Pipeline
+ # ensure that the inputs are unicode (for Python 2)
+ if sys.version_info[0] == 2:
+ source = Utils.decode_filename(source)
+ if full_module_name:
+ full_module_name = Utils.decode_filename(full_module_name)
+
source_ext = os.path.splitext(source)[1]
- options.configure_language_defaults(source_ext[1:]) # py/pyx
+ options.configure_language_defaults(source_ext[1:]) # py/pyx
if context is None:
- context = options.create_context()
+ context = Context.from_options(options)
# Set up source object
cwd = os.getcwd()
abs_path = os.path.abspath(source)
full_module_name = full_module_name or context.extract_module_name(source, options)
+ full_module_name = EncodedString(full_module_name)
+
+ Utils.raise_error_if_module_name_forbidden(full_module_name)
if options.relative_path_in_code_position_comments:
rel_path = full_module_name.replace('.', os.sep) + source_ext
if not abs_path.endswith(rel_path):
- rel_path = source # safety measure to prevent printing incorrect paths
+ rel_path = source # safety measure to prevent printing incorrect paths
else:
rel_path = abs_path
source_desc = FileSourceDescriptor(abs_path, rel_path)
@@ -481,8 +485,7 @@
html_filename = os.path.splitext(result.c_file)[0] + ".html"
if os.path.exists(html_filename):
with io.open(html_filename, "r", encoding="UTF-8") as html_file:
- line = html_file.readline()
- if line.startswith(u' State %d\n" % (key, state['number']))
@@ -229,6 +215,7 @@
if state:
file.write(" %s --> State %d\n" % (key, state['number']))
+ @cython.locals(char_list=list, i=cython.Py_ssize_t, n=cython.Py_ssize_t, c1=cython.long, c2=cython.long)
def chars_to_ranges(self, char_list):
char_list.sort()
i = 0
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Regexps.py cython-0.20.1+1~202203241016-9537/Cython/Plex/Regexps.py
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Regexps.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Regexps.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,21 +1,16 @@
-#=======================================================================
-#
-# Python Lexical Analyser
-#
-# Regular Expressions
-#
-#=======================================================================
+"""
+Python Lexical Analyser
+Regular Expressions
+"""
from __future__ import absolute_import
import types
-try:
- from sys import maxsize as maxint
-except ImportError:
- from sys import maxint
from . import Errors
+maxint = 2**31-1 # sentinel value
+
#
# Constants
#
@@ -186,37 +181,6 @@
# These are the basic REs from which all others are built.
#
-## class Char(RE):
-## """
-## Char(c) is an RE which matches the character |c|.
-## """
-
-## nullable = 0
-
-## def __init__(self, char):
-## self.char = char
-## self.match_nl = char == '\n'
-
-## def build_machine(self, m, initial_state, final_state, match_bol, nocase):
-## c = self.char
-## if match_bol and c != BOL:
-## s1 = self.build_opt(m, initial_state, BOL)
-## else:
-## s1 = initial_state
-## if c == '\n' or c == EOF:
-## s1 = self.build_opt(m, s1, EOL)
-## if len(c) == 1:
-## code = ord(self.char)
-## s1.add_transition((code, code+1), final_state)
-## if nocase and is_letter_code(code):
-## code2 = other_case_code(code)
-## s1.add_transition((code2, code2+1), final_state)
-## else:
-## s1.add_transition(c, final_state)
-
-## def calc_str(self):
-## return "Char(%s)" % repr(self.char)
-
def Char(c):
"""
@@ -428,6 +392,7 @@
name = "Case"
return "%s(%s)" % (name, self.re)
+
#
# Composite RE constructors
# -------------------------
@@ -469,7 +434,6 @@
"""
Any(s) is an RE which matches any character in the string |s|.
"""
- #result = apply(Alt, tuple(map(Char, s)))
result = CodeRanges(chars_to_ranges(s))
result.str = "Any(%s)" % repr(s)
return result
@@ -549,6 +513,7 @@
"""
return SwitchCase(re, nocase=0)
+
#
# RE Constants
#
@@ -573,4 +538,3 @@
Eof is an RE which matches the end of the file.
"""
Eof.str = "Eof"
-
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Scanners.pxd cython-0.20.1+1~202203241016-9537/Cython/Plex/Scanners.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Scanners.pxd 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Scanners.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -29,17 +29,18 @@
cdef public level
@cython.locals(input_state=long)
- cdef next_char(self)
+ cdef inline next_char(self)
@cython.locals(action=Action)
cpdef tuple read(self)
- cdef tuple scan_a_token(self)
- cdef tuple position(self)
+ cdef inline tuple scan_a_token(self)
+ ##cdef tuple position(self) # used frequently by Parsing.py
- @cython.locals(cur_pos=long, cur_line=long, cur_line_start=long,
- input_state=long, next_pos=long, state=dict,
- buf_start_pos=long, buf_len=long, buf_index=long,
- trace=bint, discard=long, data=unicode, buffer=unicode)
+ @cython.final
+ @cython.locals(cur_pos=Py_ssize_t, cur_line=Py_ssize_t, cur_line_start=Py_ssize_t,
+ input_state=long, next_pos=Py_ssize_t, state=dict,
+ buf_start_pos=Py_ssize_t, buf_len=Py_ssize_t, buf_index=Py_ssize_t,
+ trace=bint, discard=Py_ssize_t, data=unicode, buffer=unicode)
cdef run_machine_inlined(self)
- cdef begin(self, state)
- cdef produce(self, value, text = *)
+ cdef inline begin(self, state)
+ cdef inline produce(self, value, text = *)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Scanners.py cython-0.20.1+1~202203241016-9537/Cython/Plex/Scanners.py
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Scanners.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Scanners.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,17 +1,15 @@
-#=======================================================================
-#
-# Python Lexical Analyser
-#
-#
-# Scanning an input stream
-#
-#=======================================================================
+# cython: language_level=3str
+# cython: auto_pickle=False
+"""
+Python Lexical Analyser
+Scanning an input stream
+"""
from __future__ import absolute_import
import cython
-cython.declare(BOL=object, EOL=object, EOF=object, NOT_FOUND=object)
+cython.declare(BOL=object, EOL=object, EOF=object, NOT_FOUND=object) # noqa:E402
from . import Errors
from .Regexps import BOL, EOL, EOF
@@ -172,26 +170,28 @@
buf_len = len(buffer)
b_action, b_cur_pos, b_cur_line, b_cur_line_start, b_cur_char, b_input_state, b_next_pos = \
None, 0, 0, 0, u'', 0, 0
+
trace = self.trace
while 1:
- if trace: #TRACE#
- print("State %d, %d/%d:%s -->" % ( #TRACE#
- state['number'], input_state, cur_pos, repr(cur_char))) #TRACE#
+ if trace:
+ print("State %d, %d/%d:%s -->" % (
+ state['number'], input_state, cur_pos, repr(cur_char)))
+
# Begin inlined self.save_for_backup()
- #action = state.action #@slow
- action = state['action'] #@fast
+ action = state['action']
if action is not None:
b_action, b_cur_pos, b_cur_line, b_cur_line_start, b_cur_char, b_input_state, b_next_pos = \
action, cur_pos, cur_line, cur_line_start, cur_char, input_state, next_pos
# End inlined self.save_for_backup()
+
c = cur_char
- #new_state = state.new_state(c) #@slow
- new_state = state.get(c, NOT_FOUND) #@fast
- if new_state is NOT_FOUND: #@fast
- new_state = c and state.get('else') #@fast
+ new_state = state.get(c, NOT_FOUND)
+ if new_state is NOT_FOUND:
+ new_state = c and state.get('else')
+
if new_state:
- if trace: #TRACE#
- print("State %d" % new_state['number']) #TRACE#
+ if trace:
+ print("State %d" % new_state['number'])
state = new_state
# Begin inlined: self.next_char()
if input_state == 1:
@@ -239,8 +239,8 @@
cur_char = u''
# End inlined self.next_char()
else: # not new_state
- if trace: #TRACE#
- print("blocked") #TRACE#
+ if trace:
+ print("blocked")
# Begin inlined: action = self.back_up()
if b_action is not None:
(action, cur_pos, cur_line, cur_line_start,
@@ -251,15 +251,16 @@
action = None
break # while 1
# End inlined: action = self.back_up()
+
self.cur_pos = cur_pos
self.cur_line = cur_line
self.cur_line_start = cur_line_start
self.cur_char = cur_char
self.input_state = input_state
self.next_pos = next_pos
- if trace: #TRACE#
- if action is not None: #TRACE#
- print("Doing %s" % action) #TRACE#
+ if trace:
+ if action is not None:
+ print("Doing %s" % action)
return action
def next_char(self):
@@ -291,7 +292,7 @@
else: # input_state = 5
self.cur_char = u''
if self.trace:
- print("--> [%d] %d %s" % (input_state, self.cur_pos, repr(self.cur_char)))
+ print("--> [%d] %d %r" % (input_state, self.cur_pos, self.cur_char))
def position(self):
"""
@@ -305,7 +306,8 @@
return (self.name, self.start_line, self.start_col)
def get_position(self):
- """Python accessible wrapper around position(), only for error reporting.
+ """
+ Python accessible wrapper around position(), only for error reporting.
"""
return self.position()
@@ -335,3 +337,4 @@
Override this method if you want something to be done at
end of file.
"""
+ pass
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Timing.py cython-0.20.1+1~202203241016-9537/Cython/Plex/Timing.py
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Timing.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Timing.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,23 +0,0 @@
-#
-# Get time in platform-dependent way
-#
-
-from __future__ import absolute_import
-
-import os
-from sys import platform, exit, stderr
-
-if platform == 'mac':
- import MacOS
- def time():
- return MacOS.GetTicks() / 60.0
- timekind = "real"
-elif hasattr(os, 'times'):
- def time():
- t = os.times()
- return t[0] + t[1]
- timekind = "cpu"
-else:
- stderr.write(
- "Don't know how to get time on platform %s\n" % repr(platform))
- exit(1)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Traditional.py cython-0.20.1+1~202203241016-9537/Cython/Plex/Traditional.py
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Traditional.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Traditional.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,158 +0,0 @@
-#=======================================================================
-#
-# Python Lexical Analyser
-#
-# Traditional Regular Expression Syntax
-#
-#=======================================================================
-
-from __future__ import absolute_import
-
-from .Regexps import Alt, Seq, Rep, Rep1, Opt, Any, AnyBut, Bol, Eol, Char
-from .Errors import PlexError
-
-
-class RegexpSyntaxError(PlexError):
- pass
-
-
-def re(s):
- """
- Convert traditional string representation of regular expression |s|
- into Plex representation.
- """
- return REParser(s).parse_re()
-
-
-class REParser(object):
- def __init__(self, s):
- self.s = s
- self.i = -1
- self.end = 0
- self.next()
-
- def parse_re(self):
- re = self.parse_alt()
- if not self.end:
- self.error("Unexpected %s" % repr(self.c))
- return re
-
- def parse_alt(self):
- """Parse a set of alternative regexps."""
- re = self.parse_seq()
- if self.c == '|':
- re_list = [re]
- while self.c == '|':
- self.next()
- re_list.append(self.parse_seq())
- re = Alt(*re_list)
- return re
-
- def parse_seq(self):
- """Parse a sequence of regexps."""
- re_list = []
- while not self.end and not self.c in "|)":
- re_list.append(self.parse_mod())
- return Seq(*re_list)
-
- def parse_mod(self):
- """Parse a primitive regexp followed by *, +, ? modifiers."""
- re = self.parse_prim()
- while not self.end and self.c in "*+?":
- if self.c == '*':
- re = Rep(re)
- elif self.c == '+':
- re = Rep1(re)
- else: # self.c == '?'
- re = Opt(re)
- self.next()
- return re
-
- def parse_prim(self):
- """Parse a primitive regexp."""
- c = self.get()
- if c == '.':
- re = AnyBut("\n")
- elif c == '^':
- re = Bol
- elif c == '$':
- re = Eol
- elif c == '(':
- re = self.parse_alt()
- self.expect(')')
- elif c == '[':
- re = self.parse_charset()
- self.expect(']')
- else:
- if c == '\\':
- c = self.get()
- re = Char(c)
- return re
-
- def parse_charset(self):
- """Parse a charset. Does not include the surrounding []."""
- char_list = []
- invert = 0
- if self.c == '^':
- invert = 1
- self.next()
- if self.c == ']':
- char_list.append(']')
- self.next()
- while not self.end and self.c != ']':
- c1 = self.get()
- if self.c == '-' and self.lookahead(1) != ']':
- self.next()
- c2 = self.get()
- for a in range(ord(c1), ord(c2) + 1):
- char_list.append(chr(a))
- else:
- char_list.append(c1)
- chars = ''.join(char_list)
- if invert:
- return AnyBut(chars)
- else:
- return Any(chars)
-
- def next(self):
- """Advance to the next char."""
- s = self.s
- i = self.i = self.i + 1
- if i < len(s):
- self.c = s[i]
- else:
- self.c = ''
- self.end = 1
-
- def get(self):
- if self.end:
- self.error("Premature end of string")
- c = self.c
- self.next()
- return c
-
- def lookahead(self, n):
- """Look ahead n chars."""
- j = self.i + n
- if j < len(self.s):
- return self.s[j]
- else:
- return ''
-
- def expect(self, c):
- """
- Expect to find character |c| at current position.
- Raises an exception otherwise.
- """
- if self.c == c:
- self.next()
- else:
- self.error("Missing %s" % repr(c))
-
- def error(self, mess):
- """Raise exception to signal syntax error in regexp."""
- raise RegexpSyntaxError("Syntax error in regexp %s at position %d: %s" % (
- repr(self.s), self.i, mess))
-
-
-
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Transitions.pxd cython-0.20.1+1~202203241016-9537/Cython/Plex/Transitions.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Transitions.pxd 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Transitions.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,22 @@
+cimport cython
+
+cdef long maxint
+
+@cython.final
+cdef class TransitionMap:
+ cdef list map
+ cdef dict special
+
+ @cython.locals(i=cython.Py_ssize_t, j=cython.Py_ssize_t)
+ cpdef add(self, event, new_state)
+
+ @cython.locals(i=cython.Py_ssize_t, j=cython.Py_ssize_t)
+ cpdef add_set(self, event, new_set)
+
+ @cython.locals(i=cython.Py_ssize_t, n=cython.Py_ssize_t, else_set=cython.bint)
+ cpdef iteritems(self)
+
+ @cython.locals(map=list, lo=cython.Py_ssize_t, mid=cython.Py_ssize_t, hi=cython.Py_ssize_t)
+ cdef split(self, long code)
+
+ cdef get_special(self, event)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Plex/Transitions.py cython-0.20.1+1~202203241016-9537/Cython/Plex/Transitions.py
--- cython-0.20.1+1~201611251650-6686/Cython/Plex/Transitions.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Plex/Transitions.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,15 +1,11 @@
-#
-# Plex - Transition Maps
-#
-# This version represents state sets directly as dicts for speed.
-#
+# cython: auto_pickle=False
+"""
+Plex - Transition Maps
-from __future__ import absolute_import
+This version represents state sets directly as dicts for speed.
+"""
-try:
- from sys import maxsize as maxint
-except ImportError:
- from sys import maxint
+maxint = 2**31-1 # sentinel value
class TransitionMap(object):
@@ -40,24 +36,19 @@
kept separately in a dictionary.
"""
- map = None # The list of codes and states
- special = None # Mapping for special events
-
def __init__(self, map=None, special=None):
if not map:
map = [-maxint, {}, maxint]
if not special:
special = {}
- self.map = map
- self.special = special
- #self.check() ###
+ self.map = map # The list of codes and states
+ self.special = special # Mapping for special events
- def add(self, event, new_state,
- TupleType=tuple):
+ def add(self, event, new_state):
"""
Add transition to |new_state| on |event|.
"""
- if type(event) is TupleType:
+ if type(event) is tuple:
code0, code1 = event
i = self.split(code0)
j = self.split(code1)
@@ -68,12 +59,11 @@
else:
self.get_special(event)[new_state] = 1
- def add_set(self, event, new_set,
- TupleType=tuple):
+ def add_set(self, event, new_set):
"""
Add transitions to the states in |new_set| on |event|.
"""
- if type(event) is TupleType:
+ if type(event) is tuple:
code0, code1 = event
i = self.split(code0)
j = self.split(code1)
@@ -84,15 +74,13 @@
else:
self.get_special(event).update(new_set)
- def get_epsilon(self,
- none=None):
+ def get_epsilon(self):
"""
Return the mapping for epsilon, or None.
"""
- return self.special.get('', none)
+ return self.special.get('')
- def iteritems(self,
- len=len):
+ def iteritems(self):
"""
Return the mapping as an iterable of ((code1, code2), state_set) and
(special_event, state_set) pairs.
@@ -119,8 +107,7 @@
# ------------------- Private methods --------------------
- def split(self, code,
- len=len, maxint=maxint):
+ def split(self, code):
"""
Search the list for the position of the split point for |code|,
inserting a new split point if necessary. Returns index |i| such
@@ -132,6 +119,7 @@
# Special case: code == map[-1]
if code == maxint:
return hi
+
# General case
lo = 0
# loop invariant: map[lo] <= code < map[hi] and hi - lo >= 2
@@ -147,7 +135,6 @@
return lo
else:
map[hi:hi] = [code, map[hi - 1].copy()]
- #self.check() ###
return hi
def get_special(self, event):
@@ -243,9 +230,5 @@
# State set manipulation functions
#
-#def merge_state_sets(set1, set2):
-# for state in set2.keys():
-# set1[state] = 1
-
def state_set_str(set):
return "[%s]" % ','.join(["S%d" % state.number for state in set])
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Runtime/refnanny.pyx cython-0.20.1+1~202203241016-9537/Cython/Runtime/refnanny.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Runtime/refnanny.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Runtime/refnanny.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -1,6 +1,6 @@
-# cython: language_level=3
+# cython: language_level=3, auto_pickle=False
-from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF, Py_XDECREF, Py_XINCREF
+from cpython.ref cimport PyObject, Py_INCREF, Py_CLEAR, Py_XDECREF, Py_XINCREF
from cpython.exc cimport PyErr_Fetch, PyErr_Restore
from cpython.pystate cimport PyThreadState_Get
@@ -10,6 +10,9 @@
reflog = []
cdef log(level, action, obj, lineno):
+ if reflog is None:
+ # can happen during finalisation
+ return
if loglevel >= level:
reflog.append((lineno, action, id(obj)))
@@ -29,7 +32,7 @@
self.refs = {} # id -> (count, [lineno])
self.errors = []
- cdef regref(self, obj, lineno, bint is_null):
+ cdef regref(self, obj, Py_ssize_t lineno, bint is_null):
log(LOG_ALL, u'regref', u"" if is_null else obj, lineno)
if is_null:
self.errors.append(f"NULL argument on line {lineno}")
@@ -39,7 +42,7 @@
self.refs[id_] = (count + 1, linenumbers)
linenumbers.append(lineno)
- cdef bint delref(self, obj, lineno, bint is_null) except -1:
+ cdef bint delref(self, obj, Py_ssize_t lineno, bint is_null) except -1:
# returns whether it is ok to do the decref operation
log(LOG_ALL, u'delref', u"" if is_null else obj, lineno)
if is_null:
@@ -50,12 +53,11 @@
if count == 0:
self.errors.append(f"Too many decrefs on line {lineno}, reference acquired on lines {linenumbers!r}")
return False
- elif count == 1:
+ if count == 1:
del self.refs[id_]
- return True
else:
self.refs[id_] = (count - 1, linenumbers)
- return True
+ return True
cdef end(self):
if self.refs:
@@ -63,121 +65,117 @@
for count, linenos in self.refs.itervalues():
msg += f"\n ({count}) acquired on lines: {u', '.join([f'{x}' for x in linenos])}"
self.errors.append(msg)
- if self.errors:
- return u"\n".join([u'REFNANNY: '+error for error in self.errors])
- else:
- return None
+ return u"\n".join([f'REFNANNY: {error}' for error in self.errors]) if self.errors else None
+
-cdef void report_unraisable(object e=None):
+cdef void report_unraisable(filename, Py_ssize_t lineno, object e=None):
try:
if e is None:
import sys
e = sys.exc_info()[1]
- print(f"refnanny raised an exception: {e}")
- except:
- pass # We absolutely cannot exit with an exception
+ print(f"refnanny raised an exception from {filename}:{lineno}: {e}")
+ finally:
+ return # We absolutely cannot exit with an exception
+
# All Python operations must happen after any existing
# exception has been fetched, in case we are called from
# exception-handling code.
-cdef PyObject* SetupContext(char* funcname, int lineno, char* filename) except NULL:
+cdef PyObject* SetupContext(char* funcname, Py_ssize_t lineno, char* filename) except NULL:
if Context is None:
# Context may be None during finalize phase.
# In that case, we don't want to be doing anything fancy
# like caching and resetting exceptions.
return NULL
cdef (PyObject*) type = NULL, value = NULL, tb = NULL, result = NULL
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
PyErr_Fetch(&type, &value, &tb)
try:
ctx = Context(funcname, lineno, filename)
Py_INCREF(ctx)
result = ctx
except Exception, e:
- report_unraisable(e)
+ report_unraisable(filename, lineno, e)
PyErr_Restore(type, value, tb)
return result
-cdef void GOTREF(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef void GOTREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
if ctx == NULL: return
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- if p_obj is NULL:
- (ctx).regref(None, lineno, True)
- else:
- (ctx).regref(p_obj, lineno, False)
- except:
- report_unraisable()
+ (ctx).regref(
+ p_obj if p_obj is not NULL else None,
+ lineno,
+ is_null=p_obj is NULL,
+ )
except:
- # __Pyx_GetException may itself raise errors
- pass
- PyErr_Restore(type, value, tb)
+ report_unraisable((ctx).filename, lineno=(ctx).start)
+ finally:
+ PyErr_Restore(type, value, tb)
+ return # swallow any exceptions
-cdef int GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef bint GIVEREF_and_report(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
if ctx == NULL: return 1
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
cdef bint decref_ok = False
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- if p_obj is NULL:
- decref_ok = (ctx).delref(None, lineno, True)
- else:
- decref_ok = (ctx).delref(p_obj, lineno, False)
- except:
- report_unraisable()
+ decref_ok = (ctx).delref(
+ p_obj if p_obj is not NULL else None,
+ lineno,
+ is_null=p_obj is NULL,
+ )
except:
- # __Pyx_GetException may itself raise errors
- pass
- PyErr_Restore(type, value, tb)
- return decref_ok
+ report_unraisable((ctx).filename, lineno=(ctx).start)
+ finally:
+ PyErr_Restore(type, value, tb)
+ return decref_ok # swallow any exceptions
-cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, int lineno):
+cdef void GIVEREF(PyObject* ctx, PyObject* p_obj, Py_ssize_t lineno):
GIVEREF_and_report(ctx, p_obj, lineno)
-cdef void INCREF(PyObject* ctx, PyObject* obj, int lineno):
+cdef void INCREF(PyObject* ctx, PyObject* obj, Py_ssize_t lineno):
Py_XINCREF(obj)
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
GOTREF(ctx, obj, lineno)
-cdef void DECREF(PyObject* ctx, PyObject* obj, int lineno):
+cdef void DECREF(PyObject* ctx, PyObject* obj, Py_ssize_t lineno):
if GIVEREF_and_report(ctx, obj, lineno):
Py_XDECREF(obj)
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
cdef void FinishContext(PyObject** ctx):
if ctx == NULL or ctx[0] == NULL: return
cdef (PyObject*) type = NULL, value = NULL, tb = NULL
cdef object errors = None
cdef Context context
- PyThreadState_Get()
+ PyThreadState_Get() # Check that we hold the GIL
PyErr_Fetch(&type, &value, &tb)
try:
- try:
- context = ctx[0]
- errors = context.end()
- if errors:
- print(f"{context.filename.decode('latin1')}: {context.name.decode('latin1')}()")
- print(errors)
- context = None
- except:
- report_unraisable()
+ context = ctx[0]
+ errors = context.end()
+ if errors:
+ print(f"{context.filename.decode('latin1')}: {context.name.decode('latin1')}()")
+ print(errors)
+ context = None
except:
- # __Pyx_GetException may itself raise errors
- pass
- Py_XDECREF(ctx[0])
- ctx[0] = NULL
- PyErr_Restore(type, value, tb)
+ report_unraisable(
+ context.filename if context is not None else None,
+ lineno=context.start if context is not None else 0,
+ )
+ finally:
+ Py_CLEAR(ctx[0])
+ PyErr_Restore(type, value, tb)
+ return # swallow any exceptions
ctypedef struct RefNannyAPIStruct:
- void (*INCREF)(PyObject*, PyObject*, int)
- void (*DECREF)(PyObject*, PyObject*, int)
- void (*GOTREF)(PyObject*, PyObject*, int)
- void (*GIVEREF)(PyObject*, PyObject*, int)
- PyObject* (*SetupContext)(char*, int, char*) except NULL
+ void (*INCREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*DECREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*GOTREF)(PyObject*, PyObject*, Py_ssize_t)
+ void (*GIVEREF)(PyObject*, PyObject*, Py_ssize_t)
+ PyObject* (*SetupContext)(char*, Py_ssize_t, char*) except NULL
void (*FinishContext)(PyObject**)
cdef RefNannyAPIStruct api
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Shadow.py cython-0.20.1+1~202203241016-9537/Cython/Shadow.py
--- cython-0.20.1+1~201611251650-6686/Cython/Shadow.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Shadow.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,7 +1,7 @@
# cython.* namespace for pure mode.
from __future__ import absolute_import
-__version__ = "0.25b0"
+__version__ = "3.0.0a10"
try:
from __builtin__ import basestring
@@ -71,7 +71,7 @@
else:
# int[8] etc.
assert int(item) == item # array size must be a plain integer
- array(base_type, item)
+ return array(base_type, item)
# END shameless copy
@@ -108,17 +108,22 @@
cclass = ccall = cfunc = _EmptyDecoratorAndManager()
returns = wraparound = boundscheck = initializedcheck = nonecheck = \
- overflowcheck = embedsignature = cdivision = cdivision_warnings = \
- always_allows_keywords = profile = linetrace = infer_type = \
+ embedsignature = cdivision = cdivision_warnings = \
+ always_allows_keywords = profile = linetrace = infer_types = \
unraisable_tracebacks = freelist = \
- lambda arg: _EmptyDecoratorAndManager()
+ lambda _: _EmptyDecoratorAndManager()
-optimization = _Optimization()
+exceptval = lambda _=None, check=True: _EmptyDecoratorAndManager()
-overflowcheck.fold = optimization.use_switch = \
- optimization.unpack_method_calls = lambda arg: _EmptyDecoratorAndManager()
+overflowcheck = lambda _: _EmptyDecoratorAndManager()
+optimize = _Optimization()
-final = internal = type_version_tag = no_gc_clear = no_gc = _empty_decorator
+overflowcheck.fold = optimize.use_switch = \
+ optimize.unpack_method_calls = lambda arg: _EmptyDecoratorAndManager()
+
+final = internal = type_version_tag = no_gc_clear = no_gc = total_ordering = _empty_decorator
+
+binding = lambda _: _empty_decorator
_cython_inline = None
@@ -141,26 +146,33 @@
# Special functions
def cdiv(a, b):
- q = a / b
- if q < 0:
- q += 1
+ if a < 0:
+ a = -a
+ b = -b
+ if b < 0:
+ return (a + b + 1) // b
+ return a // b
def cmod(a, b):
r = a % b
- if (a*b) < 0:
+ if (a * b) < 0 and r:
r -= b
return r
# Emulated language constructs
-def cast(type, *args, **kwargs):
+def cast(t, *args, **kwargs):
kwargs.pop('typecheck', None)
assert not kwargs
- if hasattr(type, '__call__'):
- return type(*args)
- else:
- return args[0]
+
+ if isinstance(t, typedef):
+ return t(*args)
+ elif isinstance(t, type): # Doesn't work with old-style classes of Python 2.x
+ if len(args) != 1 or not (args[0] is None or isinstance(args[0], t)):
+ return t(*args)
+
+ return args[0]
def sizeof(arg):
return 1
@@ -172,18 +184,30 @@
def address(arg):
return pointer(type(arg))([arg])
-def declare(type=None, value=_Unspecified, **kwds):
- if type not in (None, object) and hasattr(type, '__call__'):
- if value is not _Unspecified:
- return type(value)
- else:
- return type()
+def _is_value_type(t):
+ if isinstance(t, typedef):
+ return _is_value_type(t._basetype)
+
+ return isinstance(t, type) and issubclass(t, (StructType, UnionType, ArrayType))
+
+def declare(t=None, value=_Unspecified, **kwds):
+ if value is not _Unspecified:
+ return cast(t, value)
+ elif _is_value_type(t):
+ return t()
else:
- return value
+ return None
class _nogil(object):
- """Support for 'with nogil' statement
+ """Support for 'with nogil' statement and @nogil decorator.
"""
+ def __call__(self, x):
+ if callable(x):
+ # Used as function decorator => return the function unchanged.
+ return x
+ # Used as conditional context manager or to create an "@nogil(True/False)" decorator => keep going.
+ return self
+
def __enter__(self):
pass
def __exit__(self, exc_class, exc, tb):
@@ -193,6 +217,7 @@
gil = _nogil()
del _nogil
+
# Emulated types
class CythonMetaType(type):
@@ -244,24 +269,45 @@
class ArrayType(PointerType):
- def __init__(self):
- self._items = [None] * self._n
+ def __init__(self, value=None):
+ if value is None:
+ self._items = [None] * self._n
+ else:
+ super(ArrayType, self).__init__(value)
class StructType(CythonType):
- def __init__(self, cast_from=_Unspecified, **data):
- if cast_from is not _Unspecified:
- # do cast
- if len(data) > 0:
- raise ValueError('Cannot accept keyword arguments when casting.')
- if type(cast_from) is not type(self):
- raise ValueError('Cannot cast from %s'%cast_from)
- for key, value in cast_from.__dict__.items():
- setattr(self, key, value)
+ def __init__(self, *posargs, **data):
+ if not (posargs or data):
+ return
+ if posargs and data:
+ raise ValueError('Cannot accept both positional and keyword arguments.')
+
+ # Allow 'cast_from' as single positional or keyword argument.
+ if data and len(data) == 1 and 'cast_from' in data:
+ cast_from = data.pop('cast_from')
+ elif len(posargs) == 1 and type(posargs[0]) is type(self):
+ cast_from, posargs = posargs[0], ()
+ elif posargs:
+ for key, arg in zip(self._members, posargs):
+ setattr(self, key, arg)
+ return
else:
for key, value in data.items():
+ if key not in self._members:
+ raise ValueError("Invalid struct attribute for %s: %s" % (
+ self.__class__.__name__, key))
setattr(self, key, value)
+ return
+
+ # do cast
+ if data:
+ raise ValueError('Cannot accept keyword arguments when casting.')
+ if type(cast_from) is not type(self):
+ raise ValueError('Cannot cast from %s' % cast_from)
+ for key, value in cast_from.__dict__.items():
+ setattr(self, key, value)
def __setattr__(self, key, value):
if key in self._members:
@@ -282,7 +328,7 @@
elif type(cast_from) is type(self):
datadict = cast_from.__dict__
else:
- raise ValueError('Cannot cast from %s'%cast_from)
+ raise ValueError('Cannot cast from %s' % cast_from)
else:
datadict = data
if len(datadict) > 1:
@@ -379,10 +425,34 @@
# Predefined types
-int_types = ['char', 'short', 'Py_UNICODE', 'int', 'Py_UCS4', 'long', 'longlong', 'Py_ssize_t', 'size_t']
-float_types = ['longdouble', 'double', 'float']
-complex_types = ['longdoublecomplex', 'doublecomplex', 'floatcomplex', 'complex']
-other_types = ['bint', 'void']
+int_types = [
+ 'char',
+ 'short',
+ 'Py_UNICODE',
+ 'int',
+ 'Py_UCS4',
+ 'long',
+ 'longlong',
+ 'Py_hash_t',
+ 'Py_ssize_t',
+ 'size_t',
+]
+float_types = [
+ 'longdouble',
+ 'double',
+ 'float',
+]
+complex_types = [
+ 'longdoublecomplex',
+ 'doublecomplex',
+ 'floatcomplex',
+ 'complex',
+]
+other_types = [
+ 'bint',
+ 'void',
+ 'Py_tss_t',
+]
to_repr = {
'longlong': 'long long',
@@ -417,14 +487,17 @@
gs[name] = typedef(py_complex, to_repr(name, name))
bint = typedef(bool, "bint")
-void = typedef(int, "void")
+void = typedef(None, "void")
+Py_tss_t = typedef(None, "Py_tss_t")
for t in int_types + float_types + complex_types + other_types:
for i in range(1, 4):
- gs["%s_%s" % ('p'*i, t)] = globals()[t]._pointer(i)
+ gs["%s_%s" % ('p'*i, t)] = gs[t]._pointer(i)
-void = typedef(None, "void")
-NULL = p_void(0)
+NULL = gs['p_void'](0)
+
+# looks like 'gs' has some users out there by now...
+#del gs
integral = floating = numeric = _FusedType()
@@ -440,7 +513,7 @@
def parallel(self, num_threads=None):
return nogil
- def prange(self, start=0, stop=None, step=1, schedule=None, nogil=False):
+ def prange(self, start=0, stop=None, step=1, nogil=False, schedule=None, chunksize=None, num_threads=None):
if stop is None:
stop = start
start = 0
@@ -452,6 +525,53 @@
# def threadsavailable(self):
# return 1
-import sys
+class CythonDotImportedFromElsewhere(object):
+ """
+ cython.dataclasses just shadows the standard library modules of the same name
+ """
+ def __init__(self, module):
+ self.__path__ = []
+ self.__file__ = None
+ self.__name__ = module
+ self.__package__ = module
+
+ def __getattr__(self, attr):
+ # we typically only expect this to be called once
+ from importlib import import_module
+ import sys
+ try:
+ mod = import_module(self.__name__)
+ except ImportError:
+ # but if they don't exist (Python is not sufficiently up-to-date) then
+ # you can't use them
+ raise AttributeError("%s: the standard library module %s is not available" %
+ (attr, self.__name__))
+ sys.modules['cython.%s' % self.__name__] = mod
+ return getattr(mod, attr)
+
+
+class CythonCImports(object):
+ """
+ Simplistic module mock to make cimports sort-of work in Python code.
+ """
+ def __init__(self, module):
+ self.__path__ = []
+ self.__file__ = None
+ self.__name__ = module
+ self.__package__ = module
+
+ def __getattr__(self, item):
+ if item.startswith('__') and item.endswith('__'):
+ raise AttributeError(item)
+ return __import__(item)
+
+
+import math, sys
sys.modules['cython.parallel'] = CythonDotParallel()
-del sys
+sys.modules['cython.cimports'] = CythonCImports('cython.cimports')
+sys.modules['cython.cimports.libc'] = CythonCImports('cython.cimports.libc')
+sys.modules['cython.cimports.libc.math'] = math
+# In pure Python mode @cython.dataclasses.dataclass and dataclass field should just
+# shadow the standard library ones (if they are available)
+dataclasses = sys.modules['cython.dataclasses'] = CythonDotImportedFromElsewhere('dataclasses')
+del math, sys
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Shadow.pyi cython-0.20.1+1~202203241016-9537/Cython/Shadow.pyi
--- cython-0.20.1+1~201611251650-6686/Cython/Shadow.pyi 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Shadow.pyi 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,102 @@
+from builtins import (int as py_int, float as py_float,
+ bool as py_bool, str as py_str, complex as py_complex)
+from typing import (Union, Dict, Any, Sequence, Optional,
+ List, TypeVar, Type, Generic)
+
+int = py_int
+long = py_int
+longlong = py_int
+short = py_int
+char = py_int
+sint = py_int
+slong = py_int
+slonglong = py_int
+sshort = py_int
+schar = py_int
+uint = py_int
+ulong = py_int
+ulonglong = py_int
+ushort = py_int
+uchar = py_int
+size_t = py_int
+Py_ssize_t = py_int
+Py_UCS4 = Union[py_int, str]
+Py_UNICODE = Union[py_int, str]
+float = py_float
+double = py_float
+longdouble = py_float
+complex = py_complex
+floatcomplex = py_complex
+doublecomplex = py_complex
+longdoublecomplex = py_complex
+bint = py_bool
+void = Union[None]
+basestring = py_str
+unicode = py_str
+
+gs: Dict[str, Any] # Should match the return type of globals()
+
+_T = TypeVar('_T')
+
+class _ArrayType(object, Generic[_T]):
+ is_array: bool
+ subtypes: Sequence[str]
+ dtype: _T
+ ndim: int
+ is_c_contig: bool
+ is_f_contig: bool
+ inner_contig: bool
+ broadcasting: Any
+
+ # broadcasting is not used, so it's not clear about its type
+ def __init__(self, dtype: _T, ndim: int, is_c_contig: bool = ...,
+ is_f_contig: bool = ..., inner_contig: bool = ...,
+ broadcasting: Any = ...) -> None: ...
+ def __repr__(self) -> str: ...
+
+class CythonTypeObject(object):
+ ...
+
+class CythonType(CythonTypeObject):
+ ...
+
+class PointerType(CythonType, Generic[_T]):
+ def __init__(
+ self,
+ value: Optional[Union[ArrayType[_T], PointerType[_T], List[_T], int]] = ...
+ ) -> None: ...
+ def __getitem__(self, ix: int) -> _T: ...
+ def __setitem__(self, ix: int, value: _T) -> None: ...
+ def __eq__(self, value: object) -> bool: ...
+ def __repr__(self) -> str: ...
+
+class ArrayType(PointerType[_T]):
+ def __init__(self) -> None: ...
+
+#class StructType(CythonType, Generic[_T]):
+# def __init__(
+# self,
+# value: List[Type[_T]] = ...
+# ) -> None: ...
+
+def index_type(
+ base_type: _T, item: Union[tuple, slice, int]) -> _ArrayType[_T]: ...
+
+def pointer(basetype: _T) -> Type[PointerType[_T]]: ...
+
+def array(basetype: _T, n: int) -> Type[ArrayType[_T]]: ...
+
+#def struct(basetype: _T) -> Type[StructType[_T]]: ...
+
+class typedef(CythonType, Generic[_T]):
+ name: str
+
+ def __init__(self, type: _T, name: Optional[str] = ...) -> None: ...
+ def __call__(self, *arg: Any) -> _T: ...
+ def __repr__(self) -> str: ...
+ __getitem__ = index_type
+
+#class _FusedType(CythonType, Generic[_T]):
+# def __init__(self) -> None: ...
+
+#def fused_type(*args: Tuple[_T]) -> Type[FusedType[_T]]: ...
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/StringIOTree.pxd cython-0.20.1+1~202203241016-9537/Cython/StringIOTree.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/StringIOTree.pxd 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/StringIOTree.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,22 @@
+cimport cython
+
+cdef object StringIO
+
+@cython.final
+cdef class StringIOTree:
+ cdef public list prepended_children
+ cdef public object stream
+ cdef public object write
+ cdef public list markers
+
+ @cython.locals(x=StringIOTree)
+ cpdef getvalue(self)
+ @cython.locals(x=StringIOTree)
+ cdef _collect_in(self, list target_list)
+ @cython.locals(child=StringIOTree)
+ cpdef copyto(self, target)
+ cpdef commit(self)
+ #def insert(self, iotree)
+ #def insertion_point(self)
+ @cython.locals(c=StringIOTree)
+ cpdef allmarkers(self)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/StringIOTree.py cython-0.20.1+1~202203241016-9537/Cython/StringIOTree.py
--- cython-0.20.1+1~201611251650-6686/Cython/StringIOTree.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/StringIOTree.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,7 +1,47 @@
+# cython: auto_pickle=False
+
+r"""
+Implements a buffer with insertion points. When you know you need to
+"get back" to a place and write more later, simply call insertion_point()
+at that spot and get a new StringIOTree object that is "left behind".
+
+EXAMPLE:
+
+>>> a = StringIOTree()
+>>> _= a.write('first\n')
+>>> b = a.insertion_point()
+>>> _= a.write('third\n')
+>>> _= b.write('second\n')
+>>> a.getvalue().split()
+['first', 'second', 'third']
+
+>>> c = b.insertion_point()
+>>> d = c.insertion_point()
+>>> _= d.write('alpha\n')
+>>> _= b.write('gamma\n')
+>>> _= c.write('beta\n')
+>>> b.getvalue().split()
+['second', 'alpha', 'beta', 'gamma']
+
+>>> try: from cStringIO import StringIO
+... except ImportError: from io import StringIO
+
+>>> i = StringIOTree()
+>>> d.insert(i)
+>>> _= i.write('inserted\n')
+>>> out = StringIO()
+>>> a.copyto(out)
+>>> out.getvalue().split()
+['first', 'second', 'alpha', 'inserted', 'beta', 'gamma', 'third']
+"""
+
+from __future__ import absolute_import #, unicode_literals
+
try:
+ # Prefer cStringIO since io.StringIO() does not support writing 'str' in Py2.
from cStringIO import StringIO
except ImportError:
- from io import StringIO # does not support writing 'str' in Py2
+ from io import StringIO
class StringIOTree(object):
@@ -18,10 +58,17 @@
self.markers = []
def getvalue(self):
- content = [x.getvalue() for x in self.prepended_children]
- content.append(self.stream.getvalue())
+ content = []
+ self._collect_in(content)
return "".join(content)
+ def _collect_in(self, target_list):
+ for x in self.prepended_children:
+ x._collect_in(target_list)
+ stream_content = self.stream.getvalue()
+ if stream_content:
+ target_list.append(stream_content)
+
def copyto(self, target):
"""Potentially cheaper than getvalue as no string concatenation
needs to happen."""
@@ -70,34 +117,47 @@
children = self.prepended_children
return [m for c in children for m in c.allmarkers()] + self.markers
+ """
+ # Print the result of allmarkers in a nice human-readable form. Use it only for debugging.
+ # Prints e.g.
+ # /path/to/source.pyx:
+ # cython line 2 maps to 3299-3343
+ # cython line 4 maps to 2236-2245 2306 3188-3201
+ # /path/to/othersource.pyx:
+ # cython line 3 maps to 1234-1270
+ # ...
+ # Note: In the example above, 3343 maps to line 2, 3344 does not.
+ def print_hr_allmarkers(self):
+ from collections import defaultdict
+ markers = self.allmarkers()
+ totmap = defaultdict(lambda: defaultdict(list))
+ for c_lineno, (cython_desc, cython_lineno) in enumerate(markers):
+ if cython_lineno > 0 and cython_desc.filename is not None:
+ totmap[cython_desc.filename][cython_lineno].append(c_lineno + 1)
+ reprstr = ""
+ if totmap == 0:
+ reprstr += "allmarkers is empty\n"
+ try:
+ sorted(totmap.items())
+ except:
+ print(totmap)
+ print(totmap.items())
+ for cython_path, filemap in sorted(totmap.items()):
+ reprstr += cython_path + ":\n"
+ for cython_lineno, c_linenos in sorted(filemap.items()):
+ reprstr += "\tcython line " + str(cython_lineno) + " maps to "
+ i = 0
+ while i < len(c_linenos):
+ reprstr += str(c_linenos[i])
+ flag = False
+ while i+1 < len(c_linenos) and c_linenos[i+1] == c_linenos[i]+1:
+ i += 1
+ flag = True
+ if flag:
+ reprstr += "-" + str(c_linenos[i]) + " "
+ i += 1
+ reprstr += "\n"
-__doc__ = r"""
-Implements a buffer with insertion points. When you know you need to
-"get back" to a place and write more later, simply call insertion_point()
-at that spot and get a new StringIOTree object that is "left behind".
-
-EXAMPLE:
-
->>> a = StringIOTree()
->>> _= a.write('first\n')
->>> b = a.insertion_point()
->>> _= a.write('third\n')
->>> _= b.write('second\n')
->>> a.getvalue().split()
-['first', 'second', 'third']
-
->>> c = b.insertion_point()
->>> d = c.insertion_point()
->>> _= d.write('alpha\n')
->>> _= b.write('gamma\n')
->>> _= c.write('beta\n')
->>> b.getvalue().split()
-['second', 'alpha', 'beta', 'gamma']
->>> i = StringIOTree()
->>> d.insert(i)
->>> _= i.write('inserted\n')
->>> out = StringIO()
->>> a.copyto(out)
->>> out.getvalue().split()
-['first', 'second', 'alpha', 'inserted', 'beta', 'gamma', 'third']
-"""
+ import sys
+ sys.stdout.write(reprstr)
+ """
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tempita/_tempita.py cython-0.20.1+1~202203241016-9537/Cython/Tempita/_tempita.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tempita/_tempita.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tempita/_tempita.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,3 +1,5 @@
+# cython: language_level=3str
+
"""
A small templating language
@@ -113,7 +115,7 @@
self.default_namespace['start_braces'] = delimeters[0]
self.default_namespace['end_braces'] = delimeters[1]
self.delimeters = delimeters
-
+
self._unicode = is_unicode(content)
if name is None and stacklevel is not None:
try:
@@ -144,9 +146,8 @@
def from_filename(cls, filename, namespace=None, encoding=None,
default_inherit=None, get_template=get_file_template):
- f = open(filename, 'rb')
- c = f.read()
- f.close()
+ with open(filename, 'rb') as f:
+ c = f.read()
if encoding:
c = c.decode(encoding)
return cls(content=c, name=filename, namespace=namespace,
@@ -335,7 +336,7 @@
if not isinstance(value, basestring_):
value = coerce_text(value)
if (is_unicode(value)
- and self.default_encoding):
+ and self.default_encoding):
value = value.encode(self.default_encoding)
except Exception as e:
e.args = (self._add_line_info(e.args[0], pos),)
@@ -723,7 +724,7 @@
else:
next_chunk = tokens[i + 1]
if (not isinstance(next_chunk, basestring_)
- or not isinstance(prev, basestring_)):
+ or not isinstance(prev, basestring_)):
continue
prev_ok = not prev or trail_whitespace_re.search(prev)
if i == 1 and not prev.strip():
@@ -735,7 +736,7 @@
or (i == len(tokens) - 2 and not next_chunk.strip()))):
if prev:
if ((i == 1 and not prev.strip())
- or prev_ok == 'last'):
+ or prev_ok == 'last'):
tokens[i - 1] = ''
else:
m = trail_whitespace_re.search(prev)
@@ -887,7 +888,7 @@
'Missing {{endif}}',
position=start, name=name)
if (isinstance(tokens[0], tuple)
- and tokens[0][0] == 'endif'):
+ and tokens[0][0] == 'endif'):
return ('cond', start) + tuple(pieces), tokens[1:]
next_chunk, tokens = parse_one_cond(tokens, name, context)
pieces.append(next_chunk)
@@ -949,7 +950,7 @@
'No {{endfor}}',
position=pos, name=name)
if (isinstance(tokens[0], tuple)
- and tokens[0][0] == 'endfor'):
+ and tokens[0][0] == 'endfor'):
return ('for', pos, vars, expr, content), tokens[1:]
next_chunk, tokens = parse_expr(tokens, name, context)
content.append(next_chunk)
@@ -1009,7 +1010,7 @@
'Missing {{enddef}}',
position=start, name=name)
if (isinstance(tokens[0], tuple)
- and tokens[0][0] == 'enddef'):
+ and tokens[0][0] == 'enddef'):
return ('def', start, func_name, sig, content), tokens[1:]
next_chunk, tokens = parse_expr(tokens, name, context)
content.append(next_chunk)
@@ -1072,7 +1073,7 @@
raise TemplateError('Invalid signature: (%s)' % sig_text,
position=pos, name=name)
if (not nest_count and
- (tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','))):
+ (tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','))):
default_expr = isolate_expression(sig_text, start_pos, end_pos)
defaults[var_name] = default_expr
sig_args.append(var_name)
@@ -1162,9 +1163,8 @@
template_content = sys.stdin.read()
template_name = ''
else:
- f = open(template_name, 'rb')
- template_content = f.read()
- f.close()
+ with open(template_name, 'rb') as f:
+ template_content = f.read()
if options.use_html:
TemplateClass = HTMLTemplate
else:
@@ -1172,9 +1172,8 @@
template = TemplateClass(template_content, name=template_name)
result = template.substitute(vars)
if options.output:
- f = open(options.output, 'wb')
- f.write(result)
- f.close()
+ with open(options.output, 'wb') as f:
+ f.write(result)
else:
sys.stdout.write(result)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tests/TestCodeWriter.py cython-0.20.1+1~202203241016-9537/Cython/Tests/TestCodeWriter.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tests/TestCodeWriter.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tests/TestCodeWriter.py 2022-03-24 10:16:46.000000000 +0000
@@ -4,7 +4,7 @@
# CythonTest uses the CodeWriter heavily, so do some checking by
# roundtripping Cython code through the test framework.
- # Note that this test is dependant upon the normal Cython parser
+ # Note that this test is dependent upon the normal Cython parser
# to generate the input trees to the CodeWriter. This save *a lot*
# of time; better to spend that time writing other tests than perfecting
# this one...
@@ -19,9 +19,9 @@
def test_print(self):
self.t(u"""
- print x, y
- print x + y ** 2
- print x, y, z,
+ print(x + y ** 2)
+ print(x, y, z)
+ print(x + y, x + y * z, x * (y + z))
""")
def test_if(self):
@@ -47,6 +47,20 @@
pass
""")
+ def test_cdef(self):
+ self.t(u"""
+ cdef f(x, y, z):
+ pass
+ cdef public void (x = 34, y = 54, z):
+ pass
+ cdef f(int *x, void *y, Value *z):
+ pass
+ cdef f(int **x, void **y, Value **z):
+ pass
+ cdef inline f(int &x, Value &z):
+ pass
+ """)
+
def test_longness_and_signedness(self):
self.t(u"def f(unsigned long long long long long int y):\n pass")
@@ -65,18 +79,50 @@
def test_for_loop(self):
self.t(u"""
for x, y, z in f(g(h(34) * 2) + 23):
- print x, y, z
+ print(x, y, z)
+ else:
+ print(43)
+ """)
+ self.t(u"""
+ for abc in (1, 2, 3):
+ print(x, y, z)
else:
- print 43
+ print(43)
+ """)
+
+ def test_while_loop(self):
+ self.t(u"""
+ while True:
+ while True:
+ while True:
+ continue
""")
def test_inplace_assignment(self):
self.t(u"x += 43")
+ def test_cascaded_assignment(self):
+ self.t(u"x = y = z = abc = 43")
+
def test_attribute(self):
self.t(u"a.x")
+ def test_return_none(self):
+ self.t(u"""
+ def f(x, y, z):
+ return
+ cdef f(x, y, z):
+ return
+ def f(x, y, z):
+ return None
+ cdef f(x, y, z):
+ return None
+ def f(x, y, z):
+ return 1234
+ cdef f(x, y, z):
+ return 1234
+ """)
+
if __name__ == "__main__":
import unittest
unittest.main()
-
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tests/TestCythonUtils.py cython-0.20.1+1~202203241016-9537/Cython/Tests/TestCythonUtils.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tests/TestCythonUtils.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tests/TestCythonUtils.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,128 @@
+import unittest
+
+from Cython.Utils import (
+ _CACHE_NAME_PATTERN, _build_cache_name, _find_cache_attributes,
+ build_hex_version, cached_method, clear_method_caches, try_finally_contextmanager)
+
+METHOD_NAME = "cached_next"
+CACHE_NAME = _build_cache_name(METHOD_NAME)
+NAMES = CACHE_NAME, METHOD_NAME
+
+class Cached(object):
+ @cached_method
+ def cached_next(self, x):
+ return next(x)
+
+
+class TestCythonUtils(unittest.TestCase):
+ def test_build_hex_version(self):
+ self.assertEqual('0x001D00A1', build_hex_version('0.29a1'))
+ self.assertEqual('0x001D03C4', build_hex_version('0.29.3rc4'))
+ self.assertEqual('0x001D00F0', build_hex_version('0.29'))
+ self.assertEqual('0x040000F0', build_hex_version('4.0'))
+
+ ############################## Cached Methods ##############################
+
+ def test_cache_method_name(self):
+ method_name = "foo"
+ cache_name = _build_cache_name(method_name)
+ match = _CACHE_NAME_PATTERN.match(cache_name)
+
+ self.assertIsNot(match, None)
+ self.assertEqual(match.group(1), method_name)
+
+ def test_requirements_for_Cached(self):
+ obj = Cached()
+
+ self.assertFalse(hasattr(obj, CACHE_NAME))
+ self.assertTrue(hasattr(obj, METHOD_NAME))
+ self.set_of_names_equal(obj, set())
+
+ def set_of_names_equal(self, obj, value):
+ self.assertEqual(set(_find_cache_attributes(obj)), value)
+
+ def test_find_cache_attributes(self):
+ obj = Cached()
+ method_name = "bar"
+ cache_name = _build_cache_name(method_name)
+
+ setattr(obj, CACHE_NAME, {})
+ setattr(obj, cache_name, {})
+
+ self.assertFalse(hasattr(obj, method_name))
+ self.set_of_names_equal(obj, {NAMES, (cache_name, method_name)})
+
+ def test_cached_method(self):
+ obj = Cached()
+ value = iter(range(3)) # iter for Py2
+ cache = {(value,): 0}
+
+ # cache args
+ self.assertEqual(obj.cached_next(value), 0)
+ self.set_of_names_equal(obj, {NAMES})
+ self.assertEqual(getattr(obj, CACHE_NAME), cache)
+
+ # use cache
+ self.assertEqual(obj.cached_next(value), 0)
+ self.set_of_names_equal(obj, {NAMES})
+ self.assertEqual(getattr(obj, CACHE_NAME), cache)
+
+ def test_clear_method_caches(self):
+ obj = Cached()
+ value = iter(range(3)) # iter for Py2
+ cache = {(value,): 1}
+
+ obj.cached_next(value) # cache args
+
+ clear_method_caches(obj)
+ self.set_of_names_equal(obj, set())
+
+ self.assertEqual(obj.cached_next(value), 1)
+ self.set_of_names_equal(obj, {NAMES})
+ self.assertEqual(getattr(obj, CACHE_NAME), cache)
+
+ def test_clear_method_caches_with_missing_method(self):
+ obj = Cached()
+ method_name = "bar"
+ cache_name = _build_cache_name(method_name)
+ names = cache_name, method_name
+
+ setattr(obj, cache_name, object())
+
+ self.assertFalse(hasattr(obj, method_name))
+ self.set_of_names_equal(obj, {names})
+
+ clear_method_caches(obj)
+ self.set_of_names_equal(obj, {names})
+
+ def test_try_finally_contextmanager(self):
+ states = []
+ @try_finally_contextmanager
+ def gen(*args, **kwargs):
+ states.append("enter")
+ yield (args, kwargs)
+ states.append("exit")
+
+ with gen(1, 2, 3, x=4) as call_args:
+ assert states == ["enter"]
+ self.assertEqual(call_args, ((1, 2, 3), {'x': 4}))
+ assert states == ["enter", "exit"]
+
+ class MyException(RuntimeError):
+ pass
+
+ del states[:]
+ with self.assertRaises(MyException):
+ with gen(1, 2, y=4) as call_args:
+ assert states == ["enter"]
+ self.assertEqual(call_args, ((1, 2), {'y': 4}))
+ raise MyException("FAIL INSIDE")
+ assert states == ["enter", "exit"]
+
+ del states[:]
+ with self.assertRaises(StopIteration):
+ with gen(1, 2, y=4) as call_args:
+ assert states == ["enter"]
+ self.assertEqual(call_args, ((1, 2), {'y': 4}))
+ raise StopIteration("STOP")
+ assert states == ["enter", "exit"]
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tests/TestJediTyper.py cython-0.20.1+1~202203241016-9537/Cython/Tests/TestJediTyper.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tests/TestJediTyper.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tests/TestJediTyper.py 2022-03-24 10:16:46.000000000 +0000
@@ -11,7 +11,7 @@
from tempfile import NamedTemporaryFile
from Cython.Compiler.ParseTreeTransforms import NormalizeTree, InterpretCompilerDirectives
-from Cython.Compiler import Main, Symtab, Visitor
+from Cython.Compiler import Main, Symtab, Visitor, Options
from Cython.TestUtils import TransformTest
TOOLS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'Tools'))
@@ -129,7 +129,7 @@
variables = types.pop((None, (1, 0)))
self.assertFalse(types)
self.assertEqual({'a': set(['list']), 'b': set(['list']), 'c': set(['list']), 'd': set(['list'])}, variables)
-
+
def test_typing_function_list(self):
code = '''\
def func(x):
@@ -149,14 +149,14 @@
code = '''\
a = dict()
b = {i: i**2 for i in range(10)}
- c = a
+ c = a
'''
types = self._test(code)
self.assertIn((None, (1, 0)), types)
variables = types.pop((None, (1, 0)))
self.assertFalse(types)
self.assertEqual({'a': set(['dict']), 'b': set(['dict']), 'c': set(['dict'])}, variables)
-
+
def test_typing_function_dict(self):
code = '''\
def func(x):
@@ -186,7 +186,7 @@
variables = types.pop((None, (1, 0)))
self.assertFalse(types)
self.assertEqual({'a': set(['set']), 'c': set(['set']), 'd': set(['set']), 'e': set(['set'])}, variables)
-
+
def test_typing_function_set(self):
code = '''\
def func(x):
@@ -210,8 +210,8 @@
"""
def setUp(self):
super(TestTypeInjection, self).setUp()
- compilation_options = Main.CompilationOptions(Main.default_options)
- ctx = compilation_options.create_context()
+ compilation_options = Options.CompilationOptions(Options.default_options)
+ ctx = Main.Context.from_options(compilation_options)
transform = InterpretCompilerDirectives(ctx, ctx.compiler_directives)
transform.module_scope = Symtab.ModuleScope('__main__', None, ctx)
self.declarations_finder = DeclarationsFinder()
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tests/TestTestUtils.py cython-0.20.1+1~202203241016-9537/Cython/Tests/TestTestUtils.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tests/TestTestUtils.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tests/TestTestUtils.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+import os.path
+import unittest
+import tempfile
+import textwrap
+import shutil
+
+from ..TestUtils import write_file, write_newer_file
+
+
+class TestTestUtils(unittest.TestCase):
+ def setUp(self):
+ super(TestTestUtils, self).setUp()
+ self.temp_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ if self.temp_dir and os.path.isdir(self.temp_dir):
+ shutil.rmtree(self.temp_dir)
+ super(TestTestUtils, self).tearDown()
+
+ def _test_path(self, filename):
+ return os.path.join(self.temp_dir, filename)
+
+ def _test_write_file(self, content, expected, **kwargs):
+ file_path = self._test_path("abcfile")
+ write_file(file_path, content, **kwargs)
+ assert os.path.isfile(file_path)
+
+ with open(file_path, 'rb') as f:
+ found = f.read()
+ assert found == expected, (repr(expected), repr(found))
+
+ def test_write_file_text(self):
+ text = u"abcüöä"
+ self._test_write_file(text, text.encode('utf8'))
+
+ def test_write_file_dedent(self):
+ text = u"""
+ A horse is a horse,
+ of course, of course,
+ And no one can talk to a horse
+ of course
+ """
+ self._test_write_file(text, textwrap.dedent(text).encode('utf8'), dedent=True)
+
+ def test_write_file_bytes(self):
+ self._test_write_file(b"ab\0c", b"ab\0c")
+
+ def test_write_newer_file(self):
+ file_path_1 = self._test_path("abcfile1.txt")
+ file_path_2 = self._test_path("abcfile2.txt")
+ write_file(file_path_1, "abc")
+ assert os.path.isfile(file_path_1)
+ write_newer_file(file_path_2, file_path_1, "xyz")
+ assert os.path.isfile(file_path_2)
+ assert os.path.getmtime(file_path_2) > os.path.getmtime(file_path_1)
+
+ def test_write_newer_file_same(self):
+ file_path = self._test_path("abcfile.txt")
+ write_file(file_path, "abc")
+ mtime = os.path.getmtime(file_path)
+ write_newer_file(file_path, file_path, "xyz")
+ assert os.path.getmtime(file_path) > mtime
+
+ def test_write_newer_file_fresh(self):
+ file_path = self._test_path("abcfile.txt")
+ assert not os.path.exists(file_path)
+ write_newer_file(file_path, file_path, "xyz")
+ assert os.path.isfile(file_path)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Tests/xmlrunner.py cython-0.20.1+1~202203241016-9537/Cython/Tests/xmlrunner.py
--- cython-0.20.1+1~201611251650-6686/Cython/Tests/xmlrunner.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Tests/xmlrunner.py 2022-03-24 10:16:46.000000000 +0000
@@ -27,12 +27,12 @@
def test_choice(self):
element = random.choice(self.seq)
- self.assert_(element in self.seq)
+ self.assertTrue(element in self.seq)
def test_sample(self):
self.assertRaises(ValueError, random.sample, self.seq, 20)
for element in random.sample(self.seq, 5):
- self.assert_(element in self.seq)
+ self.assertTrue(element in self.seq)
if __name__ == '__main__':
unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports'))
@@ -43,7 +43,7 @@
import os
import sys
import time
-from unittest import TestResult, _TextTestResult, TextTestRunner
+from unittest import TestResult, TextTestResult, TextTestRunner
import xml.dom.minidom
try:
from StringIO import StringIO
@@ -95,7 +95,7 @@
self.err, self.test_method)
-class _XMLTestResult(_TextTestResult):
+class _XMLTestResult(TextTestResult):
"""A test result class that can express test results in a XML report.
Used by XMLTestRunner.
@@ -103,14 +103,13 @@
def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1,
elapsed_times=True):
"Create a new instance of _XMLTestResult."
- _TextTestResult.__init__(self, stream, descriptions, verbosity)
+ TextTestResult.__init__(self, stream, descriptions, verbosity)
self.successes = []
self.callback = None
self.elapsed_times = elapsed_times
self.output_patched = False
- def _prepare_callback(self, test_info, target_list, verbose_str,
- short_str):
+ def _prepare_callback(self, test_info, target_list, verbose_str, short_str):
"""Append a _TestInfo to the given target list and sets a callback
method to be called by stopTest method.
"""
@@ -125,7 +124,7 @@
self.start_time = self.stop_time = 0
if self.showAll:
- self.stream.writeln('(%.3fs) %s' % \
+ self.stream.writeln('(%.3fs) %s' %
(test_info.get_elapsed_time(), verbose_str))
elif self.dots:
self.stream.write(short_str)
@@ -159,7 +158,7 @@
def stopTest(self, test):
"Called after execute each test method."
self._restore_standard_output()
- _TextTestResult.stopTest(self, test)
+ TextTestResult.stopTest(self, test)
self.stop_time = time.time()
if self.callback and callable(self.callback):
@@ -300,8 +299,7 @@
"Generates the XML reports to a given XMLTestRunner object."
all_results = self._get_info_by_testcase()
- if type(test_runner.output) == str and not \
- os.path.exists(test_runner.output):
+ if isinstance(test_runner.output, str) and not os.path.exists(test_runner.output):
os.makedirs(test_runner.output)
for suite, tests in all_results.items():
@@ -321,7 +319,7 @@
xml_content = doc.toprettyxml(indent='\t')
if type(test_runner.output) is str:
- report_file = open('%s%sTEST-%s.xml' % \
+ report_file = open('%s%sTEST-%s.xml' %
(test_runner.output, os.sep, suite), 'w')
try:
report_file.write(xml_content)
@@ -348,7 +346,7 @@
"""Create the TestResult object which will be used to store
information about the executed tests.
"""
- return _XMLTestResult(self.stream, self.descriptions, \
+ return _XMLTestResult(self.stream, self.descriptions,
self.verbosity, self.elapsed_times)
def run(self, test):
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/TestUtils.py cython-0.20.1+1~202203241016-9537/Cython/TestUtils.py
--- cython-0.20.1+1~201611251650-6686/Cython/TestUtils.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/TestUtils.py 2022-03-24 10:16:46.000000000 +0000
@@ -2,7 +2,11 @@
import os
import unittest
+import shlex
+import sys
import tempfile
+import textwrap
+from io import open
from .Compiler import Errors
from .CodeWriter import CodeWriter
@@ -47,13 +51,10 @@
class CythonTest(unittest.TestCase):
def setUp(self):
- self.listing_file = Errors.listing_file
- self.echo_file = Errors.echo_file
- Errors.listing_file = Errors.echo_file = None
+ Errors.init_thread()
def tearDown(self):
- Errors.listing_file = self.listing_file
- Errors.echo_file = self.echo_file
+ Errors.init_thread()
def assertLines(self, expected, result):
"Checks that the given strings or lists of strings are equal line by line"
@@ -177,41 +178,97 @@
if TreePath.find_first(node, path) is not None:
Errors.error(
node.pos,
- "Unexpected path '%s' found in result tree" % path)
+ "Unexpected path '%s' found in result tree" % path)
self.visitchildren(node)
return node
visit_Node = VisitorTransform.recurse_to_children
-def unpack_source_tree(tree_file, dir=None):
- if dir is None:
- dir = tempfile.mkdtemp()
- header = []
- cur_file = None
- f = open(tree_file)
- try:
- lines = f.readlines()
- finally:
- f.close()
- del f
+def unpack_source_tree(tree_file, workdir, cython_root):
+ programs = {
+ 'PYTHON': [sys.executable],
+ 'CYTHON': [sys.executable, os.path.join(cython_root, 'cython.py')],
+ 'CYTHONIZE': [sys.executable, os.path.join(cython_root, 'cythonize.py')]
+ }
+
+ if workdir is None:
+ workdir = tempfile.mkdtemp()
+ header, cur_file = [], None
+ with open(tree_file, 'rb') as f:
+ try:
+ for line in f:
+ if line[:5] == b'#####':
+ filename = line.strip().strip(b'#').strip().decode('utf8').replace('/', os.path.sep)
+ path = os.path.join(workdir, filename)
+ if not os.path.exists(os.path.dirname(path)):
+ os.makedirs(os.path.dirname(path))
+ if cur_file is not None:
+ to_close, cur_file = cur_file, None
+ to_close.close()
+ cur_file = open(path, 'wb')
+ elif cur_file is not None:
+ cur_file.write(line)
+ elif line.strip() and not line.lstrip().startswith(b'#'):
+ if line.strip() not in (b'"""', b"'''"):
+ command = shlex.split(line.decode('utf8'))
+ if not command: continue
+ # In Python 3: prog, *args = command
+ prog, args = command[0], command[1:]
+ try:
+ header.append(programs[prog]+args)
+ except KeyError:
+ header.append(command)
+ finally:
+ if cur_file is not None:
+ cur_file.close()
+ return workdir, header
+
+
+def write_file(file_path, content, dedent=False, encoding=None):
+ r"""Write some content (text or bytes) to the file
+ at `file_path` without translating `'\n'` into `os.linesep`.
+
+ The default encoding is `'utf-8'`.
+ """
+ if isinstance(content, bytes):
+ mode = "wb"
+
+ # binary mode doesn't take an encoding and newline arguments
+ newline = None
+ default_encoding = None
+ else:
+ mode = "w"
+
+ # any "\n" characters written are not translated
+ # to the system default line separator, os.linesep
+ newline = "\n"
+ default_encoding = "utf-8"
+
+ if encoding is None:
+ encoding = default_encoding
+
+ if dedent:
+ content = textwrap.dedent(content)
+
+ with open(file_path, mode=mode, encoding=encoding, newline=newline) as f:
+ f.write(content)
+
+
+def write_newer_file(file_path, newer_than, content, dedent=False, encoding=None):
+ r"""
+ Write `content` to the file `file_path` without translating `'\n'`
+ into `os.linesep` and make sure it is newer than the file `newer_than`.
+
+ The default encoding is `'utf-8'` (same as for `write_file`).
+ """
+ write_file(file_path, content, dedent=dedent, encoding=encoding)
+
try:
- for line in lines:
- if line[:5] == '#####':
- filename = line.strip().strip('#').strip().replace('/', os.path.sep)
- path = os.path.join(dir, filename)
- if not os.path.exists(os.path.dirname(path)):
- os.makedirs(os.path.dirname(path))
- if cur_file is not None:
- f, cur_file = cur_file, None
- f.close()
- cur_file = open(path, 'w')
- elif cur_file is not None:
- cur_file.write(line)
- elif line.strip() and not line.lstrip().startswith('#'):
- if line.strip() not in ('"""', "'''"):
- header.append(line)
- finally:
- if cur_file is not None:
- cur_file.close()
- return dir, ''.join(header)
+ other_time = os.path.getmtime(newer_than)
+ except OSError:
+ # Support writing a fresh file (which is always newer than a non-existant one)
+ other_time = None
+
+ while other_time is None or other_time >= os.path.getmtime(file_path):
+ write_file(file_path, content, dedent=dedent, encoding=encoding)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/arrayarray.h cython-0.20.1+1~202203241016-9537/Cython/Utility/arrayarray.h
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/arrayarray.h 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/arrayarray.h 2022-03-24 10:16:46.000000000 +0000
@@ -29,7 +29,7 @@
int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *);
#if PY_MAJOR_VERSION >= 3
char *formats;
-#endif
+#endif
} arraydescr;
@@ -47,6 +47,10 @@
char *as_chars;
unsigned long *as_ulongs;
long *as_longs;
+#if PY_MAJOR_VERSION >= 3
+ unsigned long long *as_ulonglongs;
+ long long *as_longlongs;
+#endif
short *as_shorts;
unsigned short *as_ushorts;
Py_UNICODE *as_pyunicodes;
@@ -84,7 +88,7 @@
op->ob_descr = descr;
op->allocated = size;
op->weakreflist = NULL;
- op->ob_size = size;
+ __Pyx_SET_SIZE(op, size);
if (size <= 0) {
op->data.ob_item = NULL;
}
@@ -110,9 +114,9 @@
if (items == NULL) {
PyErr_NoMemory();
return -1;
- }
+ }
self->data.ob_item = (char*) items;
- self->ob_size = n;
+ __Pyx_SET_SIZE(self, n);
self->allocated = n;
return 0;
}
@@ -121,12 +125,12 @@
static CYTHON_INLINE int resize_smart(arrayobject *self, Py_ssize_t n) {
void *items = (void*) self->data.ob_item;
Py_ssize_t newsize;
- if (n < self->ob_size) {
- self->ob_size = n;
+ if (n < self->allocated && n*4 > self->allocated) {
+ __Pyx_SET_SIZE(self, n);
return 0;
}
newsize = n + (n / 2) + 1;
- if (newsize <= self->allocated) { /* overflow */
+ if (newsize <= n) { /* overflow */
PyErr_NoMemory();
return -1;
}
@@ -134,9 +138,9 @@
if (items == NULL) {
PyErr_NoMemory();
return -1;
- }
+ }
self->data.ob_item = (char*) items;
- self->ob_size = n;
+ __Pyx_SET_SIZE(self, n);
self->allocated = newsize;
return 0;
}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/AsyncGen.c cython-0.20.1+1~202203241016-9537/Cython/Utility/AsyncGen.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/AsyncGen.c 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/AsyncGen.c 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,1284 @@
+// This is copied from genobject.c in CPython 3.6.
+// Try to keep it in sync by doing this from time to time:
+// sed -e 's|__pyx_||ig' Cython/Utility/AsyncGen.c | diff -udw - cpython/Objects/genobject.c | less
+
+//////////////////// AsyncGenerator.proto ////////////////////
+//@requires: Coroutine.c::Coroutine
+
+#define __Pyx_AsyncGen_USED
+typedef struct {
+ __pyx_CoroutineObject coro;
+ PyObject *ag_finalizer;
+ int ag_hooks_inited;
+ int ag_closed;
+ int ag_running_async;
+} __pyx_PyAsyncGenObject;
+
+static PyTypeObject *__pyx__PyAsyncGenWrappedValueType = 0;
+static PyTypeObject *__pyx__PyAsyncGenASendType = 0;
+static PyTypeObject *__pyx__PyAsyncGenAThrowType = 0;
+static PyTypeObject *__pyx_AsyncGenType = 0;
+
+#define __Pyx_AsyncGen_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_AsyncGenType)
+#define __pyx_PyAsyncGenASend_CheckExact(o) \
+ __Pyx_IS_TYPE(o, __pyx__PyAsyncGenASendType)
+#define __pyx_PyAsyncGenAThrow_CheckExact(o) \
+ __Pyx_IS_TYPE(o, __pyx__PyAsyncGenAThrowType)
+
+static PyObject *__Pyx_async_gen_anext(PyObject *o);
+static CYTHON_INLINE PyObject *__Pyx_async_gen_asend_iternext(PyObject *o);
+static PyObject *__Pyx_async_gen_asend_send(PyObject *o, PyObject *arg);
+static PyObject *__Pyx_async_gen_asend_close(PyObject *o, PyObject *args);
+static PyObject *__Pyx_async_gen_athrow_close(PyObject *o, PyObject *args);
+
+static PyObject *__Pyx__PyAsyncGenValueWrapperNew(PyObject *val);
+
+
+static __pyx_CoroutineObject *__Pyx_AsyncGen_New(
+ __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
+ __pyx_PyAsyncGenObject *gen = PyObject_GC_New(__pyx_PyAsyncGenObject, __pyx_AsyncGenType);
+ if (unlikely(!gen))
+ return NULL;
+ gen->ag_finalizer = NULL;
+ gen->ag_closed = 0;
+ gen->ag_hooks_inited = 0;
+ gen->ag_running_async = 0;
+ return __Pyx__Coroutine_NewInit((__pyx_CoroutineObject*)gen, body, code, closure, name, qualname, module_name);
+}
+
+static int __pyx_AsyncGen_init(PyObject *module);
+static void __Pyx_PyAsyncGen_Fini(void);
+
+//////////////////// AsyncGenerator.cleanup ////////////////////
+
+__Pyx_PyAsyncGen_Fini();
+
+//////////////////// AsyncGeneratorInitFinalizer ////////////////////
+
+// this is separated out because it needs more adaptation
+
+#if PY_VERSION_HEX < 0x030600B0
+static int __Pyx_async_gen_init_hooks(__pyx_PyAsyncGenObject *o) {
+#if 0
+ // TODO: implement finalizer support in older Python versions
+ PyThreadState *tstate;
+ PyObject *finalizer;
+ PyObject *firstiter;
+#endif
+
+ if (likely(o->ag_hooks_inited)) {
+ return 0;
+ }
+
+ o->ag_hooks_inited = 1;
+
+#if 0
+ tstate = __Pyx_PyThreadState_Current;
+
+ finalizer = tstate->async_gen_finalizer;
+ if (finalizer) {
+ Py_INCREF(finalizer);
+ o->ag_finalizer = finalizer;
+ }
+
+ firstiter = tstate->async_gen_firstiter;
+ if (firstiter) {
+ PyObject *res;
+
+ Py_INCREF(firstiter);
+ res = __Pyx_PyObject_CallOneArg(firstiter, (PyObject*)o);
+ Py_DECREF(firstiter);
+ if (res == NULL) {
+ return 1;
+ }
+ Py_DECREF(res);
+ }
+#endif
+
+ return 0;
+}
+#endif
+
+
+//////////////////// AsyncGenerator ////////////////////
+//@requires: AsyncGeneratorInitFinalizer
+//@requires: Coroutine.c::Coroutine
+//@requires: Coroutine.c::ReturnWithStopIteration
+//@requires: ObjectHandling.c::PyObjectCall2Args
+//@requires: ObjectHandling.c::PyObject_GenericGetAttrNoDict
+
+PyDoc_STRVAR(__Pyx_async_gen_send_doc,
+"send(arg) -> send 'arg' into generator,\n\
+return next yielded value or raise StopIteration.");
+
+PyDoc_STRVAR(__Pyx_async_gen_close_doc,
+"close() -> raise GeneratorExit inside generator.");
+
+PyDoc_STRVAR(__Pyx_async_gen_throw_doc,
+"throw(typ[,val[,tb]]) -> raise exception in generator,\n\
+return next yielded value or raise StopIteration.");
+
+PyDoc_STRVAR(__Pyx_async_gen_await_doc,
+"__await__() -> return a representation that can be passed into the 'await' expression.");
+
+// COPY STARTS HERE:
+
+static PyObject *__Pyx_async_gen_asend_new(__pyx_PyAsyncGenObject *, PyObject *);
+static PyObject *__Pyx_async_gen_athrow_new(__pyx_PyAsyncGenObject *, PyObject *);
+
+static const char *__Pyx_NON_INIT_CORO_MSG = "can't send non-None value to a just-started coroutine";
+static const char *__Pyx_ASYNC_GEN_IGNORED_EXIT_MSG = "async generator ignored GeneratorExit";
+static const char *__Pyx_ASYNC_GEN_CANNOT_REUSE_SEND_MSG = "cannot reuse already awaited __anext__()/asend()";
+static const char *__Pyx_ASYNC_GEN_CANNOT_REUSE_CLOSE_MSG = "cannot reuse already awaited aclose()/athrow()";
+
+typedef enum {
+ __PYX_AWAITABLE_STATE_INIT, /* new awaitable, has not yet been iterated */
+ __PYX_AWAITABLE_STATE_ITER, /* being iterated */
+ __PYX_AWAITABLE_STATE_CLOSED, /* closed */
+} __pyx_AwaitableState;
+
+typedef struct {
+ PyObject_HEAD
+ __pyx_PyAsyncGenObject *ags_gen;
+
+ /* Can be NULL, when in the __anext__() mode (equivalent of "asend(None)") */
+ PyObject *ags_sendval;
+
+ __pyx_AwaitableState ags_state;
+} __pyx_PyAsyncGenASend;
+
+
+typedef struct {
+ PyObject_HEAD
+ __pyx_PyAsyncGenObject *agt_gen;
+
+ /* Can be NULL, when in the "aclose()" mode (equivalent of "athrow(GeneratorExit)") */
+ PyObject *agt_args;
+
+ __pyx_AwaitableState agt_state;
+} __pyx_PyAsyncGenAThrow;
+
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *agw_val;
+} __pyx__PyAsyncGenWrappedValue;
+
+
+#ifndef _PyAsyncGen_MAXFREELIST
+#define _PyAsyncGen_MAXFREELIST 80
+#endif
+
+// Freelists boost performance 6-10%; they also reduce memory
+// fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend
+// are short-living objects that are instantiated for every
+// __anext__ call.
+
+static __pyx__PyAsyncGenWrappedValue *__Pyx_ag_value_freelist[_PyAsyncGen_MAXFREELIST];
+static int __Pyx_ag_value_freelist_free = 0;
+
+static __pyx_PyAsyncGenASend *__Pyx_ag_asend_freelist[_PyAsyncGen_MAXFREELIST];
+static int __Pyx_ag_asend_freelist_free = 0;
+
+#define __pyx__PyAsyncGenWrappedValue_CheckExact(o) \
+ __Pyx_IS_TYPE(o, __pyx__PyAsyncGenWrappedValueType)
+
+
+static int
+__Pyx_async_gen_traverse(__pyx_PyAsyncGenObject *gen, visitproc visit, void *arg)
+{
+ Py_VISIT(gen->ag_finalizer);
+ return __Pyx_Coroutine_traverse((__pyx_CoroutineObject*)gen, visit, arg);
+}
+
+
+static PyObject *
+__Pyx_async_gen_repr(__pyx_CoroutineObject *o)
+{
+ // avoid NULL pointer dereference for qualname during garbage collection
+ return PyUnicode_FromFormat("",
+ o->gi_qualname ? o->gi_qualname : Py_None, o);
+}
+
+
+#if PY_VERSION_HEX >= 0x030600B0
+static int
+__Pyx_async_gen_init_hooks(__pyx_PyAsyncGenObject *o)
+{
+ PyThreadState *tstate;
+ PyObject *finalizer;
+ PyObject *firstiter;
+
+ if (o->ag_hooks_inited) {
+ return 0;
+ }
+
+ o->ag_hooks_inited = 1;
+
+ tstate = __Pyx_PyThreadState_Current;
+
+ finalizer = tstate->async_gen_finalizer;
+ if (finalizer) {
+ Py_INCREF(finalizer);
+ o->ag_finalizer = finalizer;
+ }
+
+ firstiter = tstate->async_gen_firstiter;
+ if (firstiter) {
+ PyObject *res;
+#if CYTHON_UNPACK_METHODS
+ PyObject *self;
+#endif
+
+ Py_INCREF(firstiter);
+ // at least asyncio stores methods here => optimise the call
+#if CYTHON_UNPACK_METHODS
+ if (likely(PyMethod_Check(firstiter)) && likely((self = PyMethod_GET_SELF(firstiter)) != NULL)) {
+ PyObject *function = PyMethod_GET_FUNCTION(firstiter);
+ res = __Pyx_PyObject_Call2Args(function, self, (PyObject*)o);
+ } else
+#endif
+ res = __Pyx_PyObject_CallOneArg(firstiter, (PyObject*)o);
+
+ Py_DECREF(firstiter);
+ if (unlikely(res == NULL)) {
+ return 1;
+ }
+ Py_DECREF(res);
+ }
+
+ return 0;
+}
+#endif
+
+
+static PyObject *
+__Pyx_async_gen_anext(PyObject *g)
+{
+ __pyx_PyAsyncGenObject *o = (__pyx_PyAsyncGenObject*) g;
+ if (unlikely(__Pyx_async_gen_init_hooks(o))) {
+ return NULL;
+ }
+ return __Pyx_async_gen_asend_new(o, NULL);
+}
+
+static PyObject *
+__Pyx_async_gen_anext_method(PyObject *g, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ return __Pyx_async_gen_anext(g);
+}
+
+
+static PyObject *
+__Pyx_async_gen_asend(__pyx_PyAsyncGenObject *o, PyObject *arg)
+{
+ if (unlikely(__Pyx_async_gen_init_hooks(o))) {
+ return NULL;
+ }
+ return __Pyx_async_gen_asend_new(o, arg);
+}
+
+
+static PyObject *
+__Pyx_async_gen_aclose(__pyx_PyAsyncGenObject *o, PyObject *arg)
+{
+ CYTHON_UNUSED_VAR(arg);
+ if (unlikely(__Pyx_async_gen_init_hooks(o))) {
+ return NULL;
+ }
+ return __Pyx_async_gen_athrow_new(o, NULL);
+}
+
+
+static PyObject *
+__Pyx_async_gen_athrow(__pyx_PyAsyncGenObject *o, PyObject *args)
+{
+ if (unlikely(__Pyx_async_gen_init_hooks(o))) {
+ return NULL;
+ }
+ return __Pyx_async_gen_athrow_new(o, args);
+}
+
+
+static PyObject *
+__Pyx_async_gen_self_method(PyObject *g, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ return __Pyx_NewRef(g);
+}
+
+
+static PyGetSetDef __Pyx_async_gen_getsetlist[] = {
+ {(char*) "__name__", (getter)__Pyx_Coroutine_get_name, (setter)__Pyx_Coroutine_set_name,
+ (char*) PyDoc_STR("name of the async generator"), 0},
+ {(char*) "__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname,
+ (char*) PyDoc_STR("qualified name of the async generator"), 0},
+ //REMOVED: {(char*) "ag_await", (getter)coro_get_cr_await, NULL,
+ //REMOVED: (char*) PyDoc_STR("object being awaited on, or None")},
+ {0, 0, 0, 0, 0} /* Sentinel */
+};
+
+static PyMemberDef __Pyx_async_gen_memberlist[] = {
+ //REMOVED: {(char*) "ag_frame", T_OBJECT, offsetof(__pyx_PyAsyncGenObject, ag_frame), READONLY},
+ {(char*) "ag_running", T_BOOL, offsetof(__pyx_PyAsyncGenObject, ag_running_async), READONLY, NULL},
+ //REMOVED: {(char*) "ag_code", T_OBJECT, offsetof(__pyx_PyAsyncGenObject, ag_code), READONLY},
+ //ADDED: "ag_await"
+ {(char*) "ag_await", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY,
+ (char*) PyDoc_STR("object being awaited on, or None")},
+ {(char *) "__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0},
+#if CYTHON_USE_TYPE_SPECS
+ {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0},
+#endif
+ {0, 0, 0, 0, 0} /* Sentinel */
+};
+
+PyDoc_STRVAR(__Pyx_async_aclose_doc,
+"aclose() -> raise GeneratorExit inside generator.");
+
+PyDoc_STRVAR(__Pyx_async_asend_doc,
+"asend(v) -> send 'v' in generator.");
+
+PyDoc_STRVAR(__Pyx_async_athrow_doc,
+"athrow(typ[,val[,tb]]) -> raise exception in generator.");
+
+PyDoc_STRVAR(__Pyx_async_aiter_doc,
+"__aiter__(v) -> return an asynchronous iterator.");
+
+PyDoc_STRVAR(__Pyx_async_anext_doc,
+"__anext__(v) -> continue asynchronous iteration and return the next element.");
+
+static PyMethodDef __Pyx_async_gen_methods[] = {
+ {"asend", (PyCFunction)__Pyx_async_gen_asend, METH_O, __Pyx_async_asend_doc},
+ {"athrow",(PyCFunction)__Pyx_async_gen_athrow, METH_VARARGS, __Pyx_async_athrow_doc},
+ {"aclose", (PyCFunction)__Pyx_async_gen_aclose, METH_NOARGS, __Pyx_async_aclose_doc},
+ {"__aiter__", (PyCFunction)__Pyx_async_gen_self_method, METH_NOARGS, __Pyx_async_aiter_doc},
+ {"__anext__", (PyCFunction)__Pyx_async_gen_anext_method, METH_NOARGS, __Pyx_async_anext_doc},
+ {0, 0, 0, 0} /* Sentinel */
+};
+
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_AsyncGenType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_am_aiter, (void *)PyObject_SelfIter},
+ {Py_am_anext, (void *)__Pyx_async_gen_anext},
+ {Py_tp_repr, (void *)__Pyx_async_gen_repr},
+ {Py_tp_traverse, (void *)__Pyx_async_gen_traverse},
+ {Py_tp_methods, (void *)__Pyx_async_gen_methods},
+ {Py_tp_members, (void *)__Pyx_async_gen_memberlist},
+ {Py_tp_getset, (void *)__Pyx_async_gen_getsetlist},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+ {0, 0},
+};
+
+static PyType_Spec __pyx_AsyncGenType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "async_generator",
+ sizeof(__pyx_PyAsyncGenObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ __pyx_AsyncGenType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
+#if CYTHON_USE_ASYNC_SLOTS
+static __Pyx_PyAsyncMethodsStruct __Pyx_async_gen_as_async = {
+ 0, /* am_await */
+ PyObject_SelfIter, /* am_aiter */
+ (unaryfunc)__Pyx_async_gen_anext, /* am_anext */
+#if PY_VERSION_HEX >= 0x030A00A3
+ 0, /*am_send*/
+#endif
+};
+#endif
+
+static PyTypeObject __pyx_AsyncGenType_type = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "async_generator", /* tp_name */
+ sizeof(__pyx_PyAsyncGenObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)__Pyx_Coroutine_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+#if CYTHON_USE_ASYNC_SLOTS
+ &__Pyx_async_gen_as_async, /* tp_as_async */
+#else
+ 0, /*tp_reserved*/
+#endif
+ (reprfunc)__Pyx_async_gen_repr, /* tp_repr */
+ 0, /* 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_HAVE_GC |
+ Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)__Pyx_async_gen_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
+ // in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
+ __Pyx_Coroutine_compare, /*tp_richcompare*/
+#else
+ 0, /*tp_richcompare*/
+#endif
+ offsetof(__pyx_CoroutineObject, gi_weakreflist), /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ __Pyx_async_gen_methods, /* tp_methods */
+ __Pyx_async_gen_memberlist, /* tp_members */
+ __Pyx_async_gen_getsetlist, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_del*/
+#else
+ __Pyx_Coroutine_del, /*tp_del*/
+#endif
+ 0, /* tp_version_tag */
+#if CYTHON_USE_TP_FINALIZE
+ __Pyx_Coroutine_del, /* tp_finalize */
+#elif PY_VERSION_HEX >= 0x030400a1
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
+};
+#endif /* CYTHON_USE_TYPE_SPECS */
+
+
+static int
+__Pyx_PyAsyncGen_ClearFreeLists(void)
+{
+ int ret = __Pyx_ag_value_freelist_free + __Pyx_ag_asend_freelist_free;
+
+ while (__Pyx_ag_value_freelist_free) {
+ __pyx__PyAsyncGenWrappedValue *o;
+ o = __Pyx_ag_value_freelist[--__Pyx_ag_value_freelist_free];
+ assert(__pyx__PyAsyncGenWrappedValue_CheckExact(o));
+ __Pyx_PyHeapTypeObject_GC_Del(o);
+ }
+
+ while (__Pyx_ag_asend_freelist_free) {
+ __pyx_PyAsyncGenASend *o;
+ o = __Pyx_ag_asend_freelist[--__Pyx_ag_asend_freelist_free];
+ assert(__Pyx_IS_TYPE(o, __pyx__PyAsyncGenASendType));
+ __Pyx_PyHeapTypeObject_GC_Del(o);
+ }
+
+ return ret;
+}
+
+static void
+__Pyx_PyAsyncGen_Fini(void)
+{
+ __Pyx_PyAsyncGen_ClearFreeLists();
+}
+
+
+static PyObject *
+__Pyx_async_gen_unwrap_value(__pyx_PyAsyncGenObject *gen, PyObject *result)
+{
+ if (result == NULL) {
+ PyObject *exc_type = PyErr_Occurred();
+ if (!exc_type) {
+ PyErr_SetNone(__Pyx_PyExc_StopAsyncIteration);
+ gen->ag_closed = 1;
+ } else if (__Pyx_PyErr_GivenExceptionMatches2(exc_type, __Pyx_PyExc_StopAsyncIteration, PyExc_GeneratorExit)) {
+ gen->ag_closed = 1;
+ }
+
+ gen->ag_running_async = 0;
+ return NULL;
+ }
+
+ if (__pyx__PyAsyncGenWrappedValue_CheckExact(result)) {
+ /* async yield */
+ __Pyx_ReturnWithStopIteration(((__pyx__PyAsyncGenWrappedValue*)result)->agw_val);
+ Py_DECREF(result);
+ gen->ag_running_async = 0;
+ return NULL;
+ }
+
+ return result;
+}
+
+
+/* ---------- Async Generator ASend Awaitable ------------ */
+
+
+static void
+__Pyx_async_gen_asend_dealloc(__pyx_PyAsyncGenASend *o)
+{
+ PyObject_GC_UnTrack((PyObject *)o);
+ Py_CLEAR(o->ags_gen);
+ Py_CLEAR(o->ags_sendval);
+ if (likely(__Pyx_ag_asend_freelist_free < _PyAsyncGen_MAXFREELIST)) {
+ assert(__pyx_PyAsyncGenASend_CheckExact(o));
+ __Pyx_ag_asend_freelist[__Pyx_ag_asend_freelist_free++] = o;
+ } else {
+ __Pyx_PyHeapTypeObject_GC_Del(o);
+ }
+}
+
+static int
+__Pyx_async_gen_asend_traverse(__pyx_PyAsyncGenASend *o, visitproc visit, void *arg)
+{
+ Py_VISIT(o->ags_gen);
+ Py_VISIT(o->ags_sendval);
+ return 0;
+}
+
+
+static PyObject *
+__Pyx_async_gen_asend_send(PyObject *g, PyObject *arg)
+{
+ __pyx_PyAsyncGenASend *o = (__pyx_PyAsyncGenASend*) g;
+ PyObject *result;
+
+ if (unlikely(o->ags_state == __PYX_AWAITABLE_STATE_CLOSED)) {
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_ASYNC_GEN_CANNOT_REUSE_SEND_MSG);
+ return NULL;
+ }
+
+ if (o->ags_state == __PYX_AWAITABLE_STATE_INIT) {
+ if (unlikely(o->ags_gen->ag_running_async)) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "anext(): asynchronous generator is already running");
+ return NULL;
+ }
+
+ if (arg == NULL || arg == Py_None) {
+ arg = o->ags_sendval ? o->ags_sendval : Py_None;
+ }
+ o->ags_state = __PYX_AWAITABLE_STATE_ITER;
+ }
+
+ o->ags_gen->ag_running_async = 1;
+ result = __Pyx_Coroutine_Send((PyObject*)o->ags_gen, arg);
+ result = __Pyx_async_gen_unwrap_value(o->ags_gen, result);
+
+ if (result == NULL) {
+ o->ags_state = __PYX_AWAITABLE_STATE_CLOSED;
+ }
+
+ return result;
+}
+
+
+static CYTHON_INLINE PyObject *
+__Pyx_async_gen_asend_iternext(PyObject *o)
+{
+ return __Pyx_async_gen_asend_send(o, Py_None);
+}
+
+
+static PyObject *
+__Pyx_async_gen_asend_throw(__pyx_PyAsyncGenASend *o, PyObject *args)
+{
+ PyObject *result;
+
+ if (unlikely(o->ags_state == __PYX_AWAITABLE_STATE_CLOSED)) {
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_ASYNC_GEN_CANNOT_REUSE_SEND_MSG);
+ return NULL;
+ }
+
+ result = __Pyx_Coroutine_Throw((PyObject*)o->ags_gen, args);
+ result = __Pyx_async_gen_unwrap_value(o->ags_gen, result);
+
+ if (result == NULL) {
+ o->ags_state = __PYX_AWAITABLE_STATE_CLOSED;
+ }
+
+ return result;
+}
+
+
+static PyObject *
+__Pyx_async_gen_asend_close(PyObject *g, PyObject *args)
+{
+ __pyx_PyAsyncGenASend *o = (__pyx_PyAsyncGenASend*) g;
+ CYTHON_UNUSED_VAR(args);
+ o->ags_state = __PYX_AWAITABLE_STATE_CLOSED;
+ Py_RETURN_NONE;
+}
+
+
+static PyMethodDef __Pyx_async_gen_asend_methods[] = {
+ {"send", (PyCFunction)__Pyx_async_gen_asend_send, METH_O, __Pyx_async_gen_send_doc},
+ {"throw", (PyCFunction)__Pyx_async_gen_asend_throw, METH_VARARGS, __Pyx_async_gen_throw_doc},
+ {"close", (PyCFunction)__Pyx_async_gen_asend_close, METH_NOARGS, __Pyx_async_gen_close_doc},
+ {"__await__", (PyCFunction)__Pyx_async_gen_self_method, METH_NOARGS, __Pyx_async_gen_await_doc},
+ {0, 0, 0, 0} /* Sentinel */
+};
+
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx__PyAsyncGenASendType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_async_gen_asend_dealloc},
+ {Py_am_await, (void *)PyObject_SelfIter},
+ {Py_tp_traverse, (void *)__Pyx_async_gen_asend_traverse},
+ {Py_tp_methods, (void *)__Pyx_async_gen_asend_methods},
+ {Py_tp_iter, (void *)PyObject_SelfIter},
+ {Py_tp_iternext, (void *)__Pyx_async_gen_asend_iternext},
+ {0, 0},
+};
+
+static PyType_Spec __pyx__PyAsyncGenASendType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "async_generator_asend",
+ sizeof(__pyx_PyAsyncGenASend),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ __pyx__PyAsyncGenASendType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
+#if CYTHON_USE_ASYNC_SLOTS
+static __Pyx_PyAsyncMethodsStruct __Pyx_async_gen_asend_as_async = {
+ PyObject_SelfIter, /* am_await */
+ 0, /* am_aiter */
+ 0, /* am_anext */
+#if PY_VERSION_HEX >= 0x030A00A3
+ 0, /*am_send*/
+#endif
+};
+#endif
+
+static PyTypeObject __pyx__PyAsyncGenASendType_type = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "async_generator_asend", /* tp_name */
+ sizeof(__pyx_PyAsyncGenASend), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)__Pyx_async_gen_asend_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+#if CYTHON_USE_ASYNC_SLOTS
+ &__Pyx_async_gen_asend_as_async, /* tp_as_async */
+#else
+ 0, /*tp_reserved*/
+#endif
+ 0, /* tp_repr */
+ 0, /* 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_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)__Pyx_async_gen_asend_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
+ // in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
+ __Pyx_Coroutine_compare, /*tp_richcompare*/
+#else
+ 0, /*tp_richcompare*/
+#endif
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)__Pyx_async_gen_asend_iternext, /* tp_iternext */
+ __Pyx_async_gen_asend_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x030400a1
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
+};
+#endif /* CYTHON_USE_TYPE_SPECS */
+
+
+static PyObject *
+__Pyx_async_gen_asend_new(__pyx_PyAsyncGenObject *gen, PyObject *sendval)
+{
+ __pyx_PyAsyncGenASend *o;
+ if (likely(__Pyx_ag_asend_freelist_free)) {
+ __Pyx_ag_asend_freelist_free--;
+ o = __Pyx_ag_asend_freelist[__Pyx_ag_asend_freelist_free];
+ _Py_NewReference((PyObject *)o);
+ } else {
+ o = PyObject_GC_New(__pyx_PyAsyncGenASend, __pyx__PyAsyncGenASendType);
+ if (unlikely(o == NULL)) {
+ return NULL;
+ }
+ }
+
+ Py_INCREF(gen);
+ o->ags_gen = gen;
+
+ Py_XINCREF(sendval);
+ o->ags_sendval = sendval;
+
+ o->ags_state = __PYX_AWAITABLE_STATE_INIT;
+
+ PyObject_GC_Track((PyObject*)o);
+ return (PyObject*)o;
+}
+
+
+/* ---------- Async Generator Value Wrapper ------------ */
+
+
+static void
+__Pyx_async_gen_wrapped_val_dealloc(__pyx__PyAsyncGenWrappedValue *o)
+{
+ PyObject_GC_UnTrack((PyObject *)o);
+ Py_CLEAR(o->agw_val);
+ if (likely(__Pyx_ag_value_freelist_free < _PyAsyncGen_MAXFREELIST)) {
+ assert(__pyx__PyAsyncGenWrappedValue_CheckExact(o));
+ __Pyx_ag_value_freelist[__Pyx_ag_value_freelist_free++] = o;
+ } else {
+ __Pyx_PyHeapTypeObject_GC_Del(o);
+ }
+}
+
+
+static int
+__Pyx_async_gen_wrapped_val_traverse(__pyx__PyAsyncGenWrappedValue *o,
+ visitproc visit, void *arg)
+{
+ Py_VISIT(o->agw_val);
+ return 0;
+}
+
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx__PyAsyncGenWrappedValueType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_async_gen_wrapped_val_dealloc},
+ {Py_tp_traverse, (void *)__Pyx_async_gen_wrapped_val_traverse},
+ {0, 0},
+};
+
+static PyType_Spec __pyx__PyAsyncGenWrappedValueType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "async_generator_wrapped_value",
+ sizeof(__pyx__PyAsyncGenWrappedValue),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ __pyx__PyAsyncGenWrappedValueType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
+static PyTypeObject __pyx__PyAsyncGenWrappedValueType_type = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "async_generator_wrapped_value", /* tp_name */
+ sizeof(__pyx__PyAsyncGenWrappedValue), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)__Pyx_async_gen_wrapped_val_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_as_async */
+ 0, /* tp_repr */
+ 0, /* 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_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)__Pyx_async_gen_wrapped_val_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x030400a1
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
+};
+#endif /* CYTHON_USE_TYPE_SPECS */
+
+
+static PyObject *
+__Pyx__PyAsyncGenValueWrapperNew(PyObject *val)
+{
+ // NOTE: steals a reference to val !
+ __pyx__PyAsyncGenWrappedValue *o;
+ assert(val);
+
+ if (likely(__Pyx_ag_value_freelist_free)) {
+ __Pyx_ag_value_freelist_free--;
+ o = __Pyx_ag_value_freelist[__Pyx_ag_value_freelist_free];
+ assert(__pyx__PyAsyncGenWrappedValue_CheckExact(o));
+ _Py_NewReference((PyObject*)o);
+ } else {
+ o = PyObject_GC_New(__pyx__PyAsyncGenWrappedValue, __pyx__PyAsyncGenWrappedValueType);
+ if (unlikely(!o)) {
+ Py_DECREF(val);
+ return NULL;
+ }
+ }
+ o->agw_val = val;
+ // no Py_INCREF(val) - steals reference!
+ PyObject_GC_Track((PyObject*)o);
+ return (PyObject*)o;
+}
+
+
+/* ---------- Async Generator AThrow awaitable ------------ */
+
+
+static void
+__Pyx_async_gen_athrow_dealloc(__pyx_PyAsyncGenAThrow *o)
+{
+ PyObject_GC_UnTrack((PyObject *)o);
+ Py_CLEAR(o->agt_gen);
+ Py_CLEAR(o->agt_args);
+ __Pyx_PyHeapTypeObject_GC_Del(o);
+}
+
+
+static int
+__Pyx_async_gen_athrow_traverse(__pyx_PyAsyncGenAThrow *o, visitproc visit, void *arg)
+{
+ Py_VISIT(o->agt_gen);
+ Py_VISIT(o->agt_args);
+ return 0;
+}
+
+
+static PyObject *
+__Pyx_async_gen_athrow_send(__pyx_PyAsyncGenAThrow *o, PyObject *arg)
+{
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*)o->agt_gen;
+ PyObject *retval, *exc_type;
+
+ if (unlikely(o->agt_state == __PYX_AWAITABLE_STATE_CLOSED)) {
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_ASYNC_GEN_CANNOT_REUSE_CLOSE_MSG);
+ return NULL;
+ }
+
+ if (unlikely(gen->resume_label == -1)) {
+ // already run past the end
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ if (o->agt_state == __PYX_AWAITABLE_STATE_INIT) {
+ if (unlikely(o->agt_gen->ag_running_async)) {
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ if (o->agt_args == NULL) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "aclose(): asynchronous generator is already running");
+ } else {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "athrow(): asynchronous generator is already running");
+ }
+ return NULL;
+ }
+
+ if (unlikely(o->agt_gen->ag_closed)) {
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ PyErr_SetNone(__Pyx_PyExc_StopAsyncIteration);
+ return NULL;
+ }
+
+ if (unlikely(arg != Py_None)) {
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_NON_INIT_CORO_MSG);
+ return NULL;
+ }
+
+ o->agt_state = __PYX_AWAITABLE_STATE_ITER;
+ o->agt_gen->ag_running_async = 1;
+
+ if (o->agt_args == NULL) {
+ /* aclose() mode */
+ o->agt_gen->ag_closed = 1;
+
+ retval = __Pyx__Coroutine_Throw((PyObject*)gen,
+ /* Do not close generator when PyExc_GeneratorExit is passed */
+ PyExc_GeneratorExit, NULL, NULL, NULL, 0);
+
+ if (retval && __pyx__PyAsyncGenWrappedValue_CheckExact(retval)) {
+ Py_DECREF(retval);
+ goto yield_close;
+ }
+ } else {
+ PyObject *typ;
+ PyObject *tb = NULL;
+ PyObject *val = NULL;
+
+ if (unlikely(!PyArg_UnpackTuple(o->agt_args, "athrow", 1, 3, &typ, &val, &tb))) {
+ return NULL;
+ }
+
+ retval = __Pyx__Coroutine_Throw((PyObject*)gen,
+ /* Do not close generator when PyExc_GeneratorExit is passed */
+ typ, val, tb, o->agt_args, 0);
+ retval = __Pyx_async_gen_unwrap_value(o->agt_gen, retval);
+ }
+ if (retval == NULL) {
+ goto check_error;
+ }
+ return retval;
+ }
+
+ assert (o->agt_state == __PYX_AWAITABLE_STATE_ITER);
+
+ retval = __Pyx_Coroutine_Send((PyObject *)gen, arg);
+ if (o->agt_args) {
+ return __Pyx_async_gen_unwrap_value(o->agt_gen, retval);
+ } else {
+ /* aclose() mode */
+ if (retval) {
+ if (unlikely(__pyx__PyAsyncGenWrappedValue_CheckExact(retval))) {
+ Py_DECREF(retval);
+ goto yield_close;
+ }
+ else {
+ return retval;
+ }
+ }
+ else {
+ goto check_error;
+ }
+ }
+
+yield_close:
+ o->agt_gen->ag_running_async = 0;
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ PyErr_SetString(
+ PyExc_RuntimeError, __Pyx_ASYNC_GEN_IGNORED_EXIT_MSG);
+ return NULL;
+
+check_error:
+ o->agt_gen->ag_running_async = 0;
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ exc_type = PyErr_Occurred();
+ if (__Pyx_PyErr_GivenExceptionMatches2(exc_type, __Pyx_PyExc_StopAsyncIteration, PyExc_GeneratorExit)) {
+ if (o->agt_args == NULL) {
+ // when aclose() is called we don't want to propagate
+ // StopAsyncIteration or GeneratorExit; just raise
+ // StopIteration, signalling that this 'aclose()' await
+ // is done.
+ PyErr_Clear();
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+ }
+ return NULL;
+}
+
+
+static PyObject *
+__Pyx_async_gen_athrow_throw(__pyx_PyAsyncGenAThrow *o, PyObject *args)
+{
+ PyObject *retval;
+
+ if (unlikely(o->agt_state == __PYX_AWAITABLE_STATE_CLOSED)) {
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_ASYNC_GEN_CANNOT_REUSE_CLOSE_MSG);
+ return NULL;
+ }
+
+ retval = __Pyx_Coroutine_Throw((PyObject*)o->agt_gen, args);
+ if (o->agt_args) {
+ return __Pyx_async_gen_unwrap_value(o->agt_gen, retval);
+ } else {
+ // aclose() mode
+ PyObject *exc_type;
+ if (unlikely(retval && __pyx__PyAsyncGenWrappedValue_CheckExact(retval))) {
+ o->agt_gen->ag_running_async = 0;
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ Py_DECREF(retval);
+ PyErr_SetString(PyExc_RuntimeError, __Pyx_ASYNC_GEN_IGNORED_EXIT_MSG);
+ return NULL;
+ }
+ exc_type = PyErr_Occurred();
+ if (__Pyx_PyErr_GivenExceptionMatches2(exc_type, __Pyx_PyExc_StopAsyncIteration, PyExc_GeneratorExit)) {
+ // when aclose() is called we don't want to propagate
+ // StopAsyncIteration or GeneratorExit; just raise
+ // StopIteration, signalling that this 'aclose()' await
+ // is done.
+ PyErr_Clear();
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+ return retval;
+ }
+}
+
+
+static PyObject *
+__Pyx_async_gen_athrow_iternext(__pyx_PyAsyncGenAThrow *o)
+{
+ return __Pyx_async_gen_athrow_send(o, Py_None);
+}
+
+
+static PyObject *
+__Pyx_async_gen_athrow_close(PyObject *g, PyObject *args)
+{
+ __pyx_PyAsyncGenAThrow *o = (__pyx_PyAsyncGenAThrow*) g;
+ CYTHON_UNUSED_VAR(args);
+ o->agt_state = __PYX_AWAITABLE_STATE_CLOSED;
+ Py_RETURN_NONE;
+}
+
+
+static PyMethodDef __Pyx_async_gen_athrow_methods[] = {
+ {"send", (PyCFunction)__Pyx_async_gen_athrow_send, METH_O, __Pyx_async_gen_send_doc},
+ {"throw", (PyCFunction)__Pyx_async_gen_athrow_throw, METH_VARARGS, __Pyx_async_gen_throw_doc},
+ {"close", (PyCFunction)__Pyx_async_gen_athrow_close, METH_NOARGS, __Pyx_async_gen_close_doc},
+ {"__await__", (PyCFunction)__Pyx_async_gen_self_method, METH_NOARGS, __Pyx_async_gen_await_doc},
+ {0, 0, 0, 0} /* Sentinel */
+};
+
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx__PyAsyncGenAThrowType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_async_gen_athrow_dealloc},
+ {Py_am_await, (void *)PyObject_SelfIter},
+ {Py_tp_traverse, (void *)__Pyx_async_gen_athrow_traverse},
+ {Py_tp_iter, (void *)PyObject_SelfIter},
+ {Py_tp_iternext, (void *)__Pyx_async_gen_athrow_iternext},
+ {Py_tp_methods, (void *)__Pyx_async_gen_athrow_methods},
+ {Py_tp_getattro, (void *)__Pyx_PyObject_GenericGetAttrNoDict},
+ {0, 0},
+};
+
+static PyType_Spec __pyx__PyAsyncGenAThrowType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "async_generator_athrow",
+ sizeof(__pyx_PyAsyncGenAThrow),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ __pyx__PyAsyncGenAThrowType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
+#if CYTHON_USE_ASYNC_SLOTS
+static __Pyx_PyAsyncMethodsStruct __Pyx_async_gen_athrow_as_async = {
+ PyObject_SelfIter, /* am_await */
+ 0, /* am_aiter */
+ 0, /* am_anext */
+#if PY_VERSION_HEX >= 0x030A00A3
+ 0, /*am_send*/
+#endif
+};
+#endif
+
+static PyTypeObject __pyx__PyAsyncGenAThrowType_type = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "async_generator_athrow", /* tp_name */
+ sizeof(__pyx_PyAsyncGenAThrow), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)__Pyx_async_gen_athrow_dealloc, /* tp_dealloc */
+ 0, /* tp_vectorcall_offset */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+#if CYTHON_USE_ASYNC_SLOTS
+ &__Pyx_async_gen_athrow_as_async, /* tp_as_async */
+#else
+ 0, /*tp_reserved*/
+#endif
+ 0, /* tp_repr */
+ 0, /* 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_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)__Pyx_async_gen_athrow_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
+ // in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
+ __Pyx_Coroutine_compare, /*tp_richcompare*/
+#else
+ 0, /*tp_richcompare*/
+#endif
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)__Pyx_async_gen_athrow_iternext, /* tp_iternext */
+ __Pyx_async_gen_athrow_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x030400a1
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
+};
+#endif /* CYTHON_USE_TYPE_SPECS */
+
+
+static PyObject *
+__Pyx_async_gen_athrow_new(__pyx_PyAsyncGenObject *gen, PyObject *args)
+{
+ __pyx_PyAsyncGenAThrow *o;
+ o = PyObject_GC_New(__pyx_PyAsyncGenAThrow, __pyx__PyAsyncGenAThrowType);
+ if (unlikely(o == NULL)) {
+ return NULL;
+ }
+ o->agt_gen = gen;
+ o->agt_args = args;
+ o->agt_state = __PYX_AWAITABLE_STATE_INIT;
+ Py_INCREF(gen);
+ Py_XINCREF(args);
+ PyObject_GC_Track((PyObject*)o);
+ return (PyObject*)o;
+}
+
+
+/* ---------- global type sharing ------------ */
+
+static int __pyx_AsyncGen_init(PyObject *module) {
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_AsyncGenType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_AsyncGenType_spec, NULL);
+#else
+ (void) module;
+ // on Windows, C-API functions can't be used in slots statically
+ __pyx_AsyncGenType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
+ __pyx_AsyncGenType = __Pyx_FetchCommonType(&__pyx_AsyncGenType_type);
+#endif
+ if (unlikely(!__pyx_AsyncGenType))
+ return -1;
+
+#if CYTHON_USE_TYPE_SPECS
+ __pyx__PyAsyncGenAThrowType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx__PyAsyncGenAThrowType_spec, NULL);
+#else
+ __pyx__PyAsyncGenAThrowType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
+ __pyx__PyAsyncGenAThrowType = __Pyx_FetchCommonType(&__pyx__PyAsyncGenAThrowType_type);
+#endif
+ if (unlikely(!__pyx__PyAsyncGenAThrowType))
+ return -1;
+
+#if CYTHON_USE_TYPE_SPECS
+ __pyx__PyAsyncGenWrappedValueType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx__PyAsyncGenWrappedValueType_spec, NULL);
+#else
+ __pyx__PyAsyncGenWrappedValueType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
+ __pyx__PyAsyncGenWrappedValueType = __Pyx_FetchCommonType(&__pyx__PyAsyncGenWrappedValueType_type);
+#endif
+ if (unlikely(!__pyx__PyAsyncGenWrappedValueType))
+ return -1;
+
+#if CYTHON_USE_TYPE_SPECS
+ __pyx__PyAsyncGenASendType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx__PyAsyncGenASendType_spec, NULL);
+#else
+ __pyx__PyAsyncGenASendType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
+ __pyx__PyAsyncGenASendType = __Pyx_FetchCommonType(&__pyx__PyAsyncGenASendType_type);
+#endif
+ if (unlikely(!__pyx__PyAsyncGenASendType))
+ return -1;
+
+ return 0;
+}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Buffer.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Buffer.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Buffer.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Buffer.c 2022-03-24 10:16:46.000000000 +0000
@@ -52,6 +52,7 @@
}
/////////////// BufferFormatStructs.proto ///////////////
+//@proto_block: utility_code_proto_before_types
#define IS_UNSIGNED(type) (((type) -1) > 0)
@@ -95,7 +96,9 @@
char is_valid_array;
} __Pyx_BufFmt_Context;
+
/////////////// GetAndReleaseBuffer.proto ///////////////
+
#if PY_MAJOR_VERSION < 3
static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags);
static void __Pyx_ReleaseBuffer(Py_buffer *view);
@@ -105,17 +108,23 @@
#endif
/////////////// GetAndReleaseBuffer ///////////////
+
#if PY_MAJOR_VERSION < 3
static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {
+ __Pyx_TypeName obj_type_name;
if (PyObject_CheckBuffer(obj)) return PyObject_GetBuffer(obj, view, flags);
{{for type_ptr, getbuffer, releasebuffer in types}}
{{if getbuffer}}
- if (PyObject_TypeCheck(obj, {{type_ptr}})) return {{getbuffer}}(obj, view, flags);
+ if (__Pyx_TypeCheck(obj, {{type_ptr}})) return {{getbuffer}}(obj, view, flags);
{{endif}}
{{endfor}}
- PyErr_Format(PyExc_TypeError, "'%.200s' does not have the buffer interface", Py_TYPE(obj)->tp_name);
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' does not have the buffer interface",
+ obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
return -1;
}
@@ -128,49 +137,111 @@
return;
}
+ if ((0)) {}
{{for type_ptr, getbuffer, releasebuffer in types}}
{{if releasebuffer}}
- if (PyObject_TypeCheck(obj, {{type_ptr}})) { {{releasebuffer}}(obj, view); return; }
+ else if (__Pyx_TypeCheck(obj, {{type_ptr}})) {{releasebuffer}}(obj, view);
{{endif}}
{{endfor}}
- Py_DECREF(obj);
view->obj = NULL;
+ Py_DECREF(obj);
}
#endif /* PY_MAJOR_VERSION < 3 */
-/////////////// BufferFormatCheck.proto ///////////////
-{{#
- Buffer format string checking
+/////////////// BufferGetAndValidate.proto ///////////////
- Buffer type checking. Utility code for checking that acquired
- buffers match our assumptions. We only need to check ndim and
- the format string; the access mode/flags is checked by the
- exporter. See:
+#define __Pyx_GetBufferAndValidate(buf, obj, dtype, flags, nd, cast, stack) \
+ ((obj == Py_None || obj == NULL) ? \
+ (__Pyx_ZeroBuffer(buf), 0) : \
+ __Pyx__GetBufferAndValidate(buf, obj, dtype, flags, nd, cast, stack))
- http://docs.python.org/3/library/struct.html
- http://legacy.python.org/dev/peps/pep-3118/#additions-to-the-struct-string-syntax
+static int __Pyx__GetBufferAndValidate(Py_buffer* buf, PyObject* obj,
+ __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack);
+static void __Pyx_ZeroBuffer(Py_buffer* buf);
+static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info);/*proto*/
- The alignment code is copied from _struct.c in Python.
-}}
+static Py_ssize_t __Pyx_minusones[] = { {{ ", ".join(["-1"] * max_dims) }} };
+static Py_ssize_t __Pyx_zeros[] = { {{ ", ".join(["0"] * max_dims) }} };
+
+
+/////////////// BufferGetAndValidate ///////////////
+//@requires: BufferFormatCheck
+
+static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
+ if (unlikely(info->buf == NULL)) return;
+ if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
+ __Pyx_ReleaseBuffer(info);
+}
+
+static void __Pyx_ZeroBuffer(Py_buffer* buf) {
+ buf->buf = NULL;
+ buf->obj = NULL;
+ buf->strides = __Pyx_zeros;
+ buf->shape = __Pyx_zeros;
+ buf->suboffsets = __Pyx_minusones;
+}
+
+static int __Pyx__GetBufferAndValidate(
+ Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags,
+ int nd, int cast, __Pyx_BufFmt_StackElem* stack)
+{
+ buf->buf = NULL;
+ if (unlikely(__Pyx_GetBuffer(obj, buf, flags) == -1)) {
+ __Pyx_ZeroBuffer(buf);
+ return -1;
+ }
+ // From this point on, we have acquired the buffer and must release it on errors.
+ if (unlikely(buf->ndim != nd)) {
+ PyErr_Format(PyExc_ValueError,
+ "Buffer has wrong number of dimensions (expected %d, got %d)",
+ nd, buf->ndim);
+ goto fail;
+ }
+ if (!cast) {
+ __Pyx_BufFmt_Context ctx;
+ __Pyx_BufFmt_Init(&ctx, stack, dtype);
+ if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
+ }
+ if (unlikely((size_t)buf->itemsize != dtype->size)) {
+ PyErr_Format(PyExc_ValueError,
+ "Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "d byte%s) does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "d byte%s)",
+ buf->itemsize, (buf->itemsize > 1) ? "s" : "",
+ dtype->name, (Py_ssize_t)dtype->size, (dtype->size > 1) ? "s" : "");
+ goto fail;
+ }
+ if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
+ return 0;
+fail:;
+ __Pyx_SafeReleaseBuffer(buf);
+ return -1;
+}
+
+
+/////////////// BufferFormatCheck.proto ///////////////
+
+// Buffer format string checking
+//
+// Buffer type checking. Utility code for checking that acquired
+// buffers match our assumptions. We only need to check ndim and
+// the format string; the access mode/flags is checked by the
+// exporter. See:
+//
+// https://docs.python.org/3/library/struct.html
+// https://www.python.org/dev/peps/pep-3118/#additions-to-the-struct-string-syntax
+//
+// The alignment code is copied from _struct.c in Python.
-static CYTHON_INLINE int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj,
- __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack);
-static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info);
static const char* __Pyx_BufFmt_CheckString(__Pyx_BufFmt_Context* ctx, const char* ts);
static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx,
__Pyx_BufFmt_StackElem* stack,
- __Pyx_TypeInfo* type); // PROTO
-
+ __Pyx_TypeInfo* type); /*proto*/
/////////////// BufferFormatCheck ///////////////
-static CYTHON_INLINE int __Pyx_IsLittleEndian(void) {
- unsigned int n = 1;
- return *(unsigned char*)(&n) != 0;
-}
-
+//@requires: ModuleSetupCode.c::IsLittleEndian
+//@requires: BufferFormatStructs
static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx,
__Pyx_BufFmt_StackElem* stack,
@@ -207,7 +278,7 @@
return -1;
} else {
count = *t++ - '0';
- while (*t >= '0' && *t < '9') {
+ while (*t >= '0' && *t <= '9') {
count *= 10;
count += *t++ - '0';
}
@@ -232,6 +303,7 @@
static const char* __Pyx_BufFmt_DescribeTypeChar(char ch, int is_complex) {
switch (ch) {
+ case '?': return "'bool'";
case 'c': return "'char'";
case 'b': return "'signed char'";
case 'B': return "'unsigned char'";
@@ -276,7 +348,7 @@
static size_t __Pyx_BufFmt_TypeCharToNativeSize(char ch, int is_complex) {
switch (ch) {
- case 'c': case 'b': case 'B': case 's': case 'p': return 1;
+ case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1;
case 'h': case 'H': return sizeof(short);
case 'i': case 'I': return sizeof(int);
case 'l': case 'L': return sizeof(long);
@@ -305,7 +377,8 @@
typedef struct { char c; PY_LONG_LONG x; } __Pyx_st_longlong;
#endif
-static size_t __Pyx_BufFmt_TypeCharToAlignment(char ch, CYTHON_UNUSED int is_complex) {
+static size_t __Pyx_BufFmt_TypeCharToAlignment(char ch, int is_complex) {
+ CYTHON_UNUSED_VAR(is_complex);
switch (ch) {
case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1;
case 'h': case 'H': return sizeof(__Pyx_st_short) - sizeof(short);
@@ -339,7 +412,8 @@
typedef struct { PY_LONG_LONG x; char c; } __Pyx_pad_longlong;
#endif
-static size_t __Pyx_BufFmt_TypeCharToPadding(char ch, CYTHON_UNUSED int is_complex) {
+static size_t __Pyx_BufFmt_TypeCharToPadding(char ch, int is_complex) {
+ CYTHON_UNUSED_VAR(is_complex);
switch (ch) {
case '?': case 'c': case 'b': case 'B': case 's': case 'p': return 1;
case 'h': case 'H': return sizeof(__Pyx_pad_short) - sizeof(short);
@@ -365,7 +439,7 @@
case 'b': case 'h': case 'i':
case 'l': case 'q': case 's': case 'p':
return 'I';
- case 'B': case 'H': case 'I': case 'L': case 'Q':
+ case '?': case 'B': case 'H': case 'I': case 'L': case 'Q':
return 'U';
case 'f': case 'd': case 'g':
return (is_complex ? 'C' : 'R');
@@ -531,13 +605,12 @@
}
/* Parse an array in the format string (e.g. (1,2,3)) */
-static CYTHON_INLINE PyObject *
+static PyObject *
__pyx_buffmt_parse_array(__Pyx_BufFmt_Context* ctx, const char** tsp)
{
const char *ts = *tsp;
- int i = 0, number;
- int ndim = ctx->head->field->type->ndim;
-;
+ int i = 0, number, ndim;
+
++ts;
if (ctx->new_count != 1) {
PyErr_SetString(PyExc_ValueError,
@@ -548,6 +621,9 @@
/* Process the previous element */
if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL;
+ // store ndim now, as field advanced by __Pyx_BufFmt_ProcessTypeChunk call
+ ndim = ctx->head->field->type->ndim;
+
/* Parse all numbers in the format string */
while (*ts && *ts != ')') {
// ignore space characters (not using isspace() due to C/C++ problem on MacOS-X)
@@ -611,7 +687,7 @@
++ts;
break;
case '<':
- if (!__Pyx_IsLittleEndian()) {
+ if (!__Pyx_Is_Little_Endian()) {
PyErr_SetString(PyExc_ValueError, "Little-endian buffer not supported on big-endian compiler");
return NULL;
}
@@ -620,7 +696,7 @@
break;
case '>':
case '!':
- if (__Pyx_IsLittleEndian()) {
+ if (__Pyx_Is_Little_Endian()) {
PyErr_SetString(PyExc_ValueError, "Big-endian buffer not supported on little-endian compiler");
return NULL;
}
@@ -685,13 +761,13 @@
__Pyx_BufFmt_RaiseUnexpectedChar('Z');
return NULL;
}
- /* fall through */
- case 'c': case 'b': case 'B': case 'h': case 'H': case 'i': case 'I':
+ CYTHON_FALLTHROUGH;
+ case '?': case 'c': case 'b': case 'B': case 'h': case 'H': case 'i': case 'I':
case 'l': case 'L': case 'q': case 'Q':
case 'f': case 'd': case 'g':
case 'O': case 'p':
- if (ctx->enc_type == *ts && got_Z == ctx->is_complex &&
- ctx->enc_packmode == ctx->new_packmode) {
+ if ((ctx->enc_type == *ts) && (got_Z == ctx->is_complex) &&
+ (ctx->enc_packmode == ctx->new_packmode) && (!ctx->is_valid_array)) {
/* Continue pooling same type */
ctx->enc_count += ctx->new_count;
ctx->new_count = 1;
@@ -699,7 +775,7 @@
++ts;
break;
}
- /* fall through */
+ CYTHON_FALLTHROUGH;
case 's':
/* 's' or new type (cannot be added to current pool) */
if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL;
@@ -729,60 +805,13 @@
}
}
-static CYTHON_INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) {
- buf->buf = NULL;
- buf->obj = NULL;
- buf->strides = __Pyx_zeros;
- buf->shape = __Pyx_zeros;
- buf->suboffsets = __Pyx_minusones;
-}
-
-static CYTHON_INLINE int __Pyx_GetBufferAndValidate(
- Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags,
- int nd, int cast, __Pyx_BufFmt_StackElem* stack)
-{
- if (obj == Py_None || obj == NULL) {
- __Pyx_ZeroBuffer(buf);
- return 0;
- }
- buf->buf = NULL;
- if (__Pyx_GetBuffer(obj, buf, flags) == -1) goto fail;
- if (buf->ndim != nd) {
- PyErr_Format(PyExc_ValueError,
- "Buffer has wrong number of dimensions (expected %d, got %d)",
- nd, buf->ndim);
- goto fail;
- }
- if (!cast) {
- __Pyx_BufFmt_Context ctx;
- __Pyx_BufFmt_Init(&ctx, stack, dtype);
- if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
- }
- if ((unsigned)buf->itemsize != dtype->size) {
- PyErr_Format(PyExc_ValueError,
- "Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "d byte%s) does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "d byte%s)",
- buf->itemsize, (buf->itemsize > 1) ? "s" : "",
- dtype->name, (Py_ssize_t)dtype->size, (dtype->size > 1) ? "s" : "");
- goto fail;
- }
- if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
- return 0;
-fail:;
- __Pyx_ZeroBuffer(buf);
- return -1;
-}
-
-static CYTHON_INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
- if (info->buf == NULL) return;
- if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
- __Pyx_ReleaseBuffer(info);
-}
-
/////////////// TypeInfoCompare.proto ///////////////
static int __pyx_typeinfo_cmp(__Pyx_TypeInfo *a, __Pyx_TypeInfo *b);
/////////////// TypeInfoCompare ///////////////
-/* See if two dtypes are equal */
+//@requires: BufferFormatStructs
+
+// See if two dtypes are equal
static int
__pyx_typeinfo_cmp(__Pyx_TypeInfo *a, __Pyx_TypeInfo *b)
{
@@ -841,7 +870,6 @@
}
-
/////////////// TypeInfoToFormat.proto ///////////////
struct __pyx_typeinfo_string {
char string[3];
@@ -849,7 +877,9 @@
static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type);
/////////////// TypeInfoToFormat ///////////////
-{{# See also MemoryView.pyx:BufferFormatFromTypeInfo }}
+//@requires: BufferFormatStructs
+
+// See also MemoryView.pyx:BufferFormatFromTypeInfo
static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type) {
struct __pyx_typeinfo_string result = { {0} };
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Builtins.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Builtins.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Builtins.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Builtins.c 2022-03-24 10:16:46.000000000 +0000
@@ -20,47 +20,7 @@
// access requires a rewrite as a dedicated class.
static PyObject* __Pyx_Globals(void) {
- Py_ssize_t i;
- PyObject *names;
- PyObject *globals = $moddict_cname;
- Py_INCREF(globals);
- names = PyObject_Dir($module_cname);
- if (!names)
- goto bad;
- for (i = PyList_GET_SIZE(names)-1; i >= 0; i--) {
-#if CYTHON_COMPILING_IN_PYPY
- PyObject* name = PySequence_ITEM(names, i);
- if (!name)
- goto bad;
-#else
- PyObject* name = PyList_GET_ITEM(names, i);
-#endif
- if (!PyDict_Contains(globals, name)) {
- PyObject* value = __Pyx_GetAttr($module_cname, name);
- if (!value) {
-#if CYTHON_COMPILING_IN_PYPY
- Py_DECREF(name);
-#endif
- goto bad;
- }
- if (PyDict_SetItem(globals, name, value) < 0) {
-#if CYTHON_COMPILING_IN_PYPY
- Py_DECREF(name);
-#endif
- Py_DECREF(value);
- goto bad;
- }
- }
-#if CYTHON_COMPILING_IN_PYPY
- Py_DECREF(name);
-#endif
- }
- Py_DECREF(names);
- return globals;
-bad:
- Py_XDECREF(names);
- Py_XDECREF(globals);
- return NULL;
+ return __Pyx_NewRef($moddict_cname);
}
//////////////////// PyExecGlobals.proto ////////////////////
@@ -68,17 +28,11 @@
static PyObject* __Pyx_PyExecGlobals(PyObject*);
//////////////////// PyExecGlobals ////////////////////
-//@requires: Globals
+//@substitute: naming
//@requires: PyExec
static PyObject* __Pyx_PyExecGlobals(PyObject* code) {
- PyObject* result;
- PyObject* globals = __Pyx_Globals();
- if (unlikely(!globals))
- return NULL;
- result = __Pyx_PyExec2(code, globals);
- Py_DECREF(globals);
- return result;
+ return __Pyx_PyExec2(code, $moddict_cname);
}
//////////////////// PyExec.proto ////////////////////
@@ -100,27 +54,31 @@
if (!globals || globals == Py_None) {
globals = $moddict_cname;
- } else if (!PyDict_Check(globals)) {
- PyErr_Format(PyExc_TypeError, "exec() arg 2 must be a dict, not %.200s",
- Py_TYPE(globals)->tp_name);
+ } else if (unlikely(!PyDict_Check(globals))) {
+ __Pyx_TypeName globals_type_name =
+ __Pyx_PyType_GetName(Py_TYPE(globals));
+ PyErr_Format(PyExc_TypeError,
+ "exec() arg 2 must be a dict, not " __Pyx_FMT_TYPENAME,
+ globals_type_name);
+ __Pyx_DECREF_TypeName(globals_type_name);
goto bad;
}
if (!locals || locals == Py_None) {
locals = globals;
}
- if (PyDict_GetItem(globals, PYIDENT("__builtins__")) == NULL) {
- if (PyDict_SetItem(globals, PYIDENT("__builtins__"), PyEval_GetBuiltins()) < 0)
+ if (__Pyx_PyDict_GetItemStr(globals, PYIDENT("__builtins__")) == NULL) {
+ if (unlikely(PyDict_SetItem(globals, PYIDENT("__builtins__"), PyEval_GetBuiltins()) < 0))
goto bad;
}
if (PyCode_Check(o)) {
- if (__Pyx_PyCode_HasFreeVars((PyCodeObject *)o)) {
+ if (unlikely(__Pyx_PyCode_HasFreeVars((PyCodeObject *)o))) {
PyErr_SetString(PyExc_TypeError,
"code object passed to exec() may not contain free variables");
goto bad;
}
- #if CYTHON_COMPILING_IN_PYPY || PY_VERSION_HEX < 0x030200B1
+ #if PY_VERSION_HEX < 0x030200B1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400)
result = PyEval_EvalCode((PyCodeObject *)o, globals, locals);
#else
result = PyEval_EvalCode(o, globals, locals);
@@ -128,19 +86,24 @@
} else {
PyCompilerFlags cf;
cf.cf_flags = 0;
+#if PY_VERSION_HEX >= 0x030800A3
+ cf.cf_feature_version = PY_MINOR_VERSION;
+#endif
if (PyUnicode_Check(o)) {
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
s = PyUnicode_AsUTF8String(o);
- if (!s) goto bad;
+ if (unlikely(!s)) goto bad;
o = s;
#if PY_MAJOR_VERSION >= 3
- } else if (!PyBytes_Check(o)) {
+ } else if (unlikely(!PyBytes_Check(o))) {
#else
- } else if (!PyString_Check(o)) {
+ } else if (unlikely(!PyString_Check(o))) {
#endif
+ __Pyx_TypeName o_type_name = __Pyx_PyType_GetName(Py_TYPE(o));
PyErr_Format(PyExc_TypeError,
- "exec: arg 1 must be string, bytes or code object, got %.200s",
- Py_TYPE(o)->tp_name);
+ "exec: arg 1 must be string, bytes or code object, got " __Pyx_FMT_TYPENAME,
+ o_type_name);
+ __Pyx_DECREF_TypeName(o_type_name);
goto bad;
}
#if PY_MAJOR_VERSION >= 3
@@ -167,20 +130,58 @@
static CYTHON_INLINE PyObject *__Pyx_GetAttr3(PyObject *, PyObject *, PyObject *); /*proto*/
//////////////////// GetAttr3 ////////////////////
-//@requires: ObjectHandling.c::GetAttr
+//@requires: ObjectHandling.c::PyObjectGetAttrStr
+//@requires: Exceptions.c::PyThreadStateGet
+//@requires: Exceptions.c::PyErrFetchRestore
+//@requires: Exceptions.c::PyErrExceptionMatches
+
+static PyObject *__Pyx_GetAttr3Default(PyObject *d) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ if (unlikely(!__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError)))
+ return NULL;
+ __Pyx_PyErr_Clear();
+ Py_INCREF(d);
+ return d;
+}
static CYTHON_INLINE PyObject *__Pyx_GetAttr3(PyObject *o, PyObject *n, PyObject *d) {
- PyObject *r = __Pyx_GetAttr(o, n);
- if (unlikely(!r)) {
- if (!PyErr_ExceptionMatches(PyExc_AttributeError))
- goto bad;
+ PyObject *r;
+#if CYTHON_USE_TYPE_SLOTS
+ if (likely(PyString_Check(n))) {
+ r = __Pyx_PyObject_GetAttrStrNoError(o, n);
+ if (unlikely(!r) && likely(!PyErr_Occurred())) {
+ r = __Pyx_NewRef(d);
+ }
+ return r;
+ }
+#endif
+ r = PyObject_GetAttr(o, n);
+ return (likely(r)) ? r : __Pyx_GetAttr3Default(d);
+}
+
+//////////////////// HasAttr.proto ////////////////////
+
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *, PyObject *); /*proto*/
+
+//////////////////// HasAttr ////////////////////
+//@requires: ObjectHandling.c::GetAttr
+
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *o, PyObject *n) {
+ PyObject *r;
+ if (unlikely(!__Pyx_PyBaseString_Check(n))) {
+ PyErr_SetString(PyExc_TypeError,
+ "hasattr(): attribute name must be string");
+ return -1;
+ }
+ r = __Pyx_GetAttr(o, n);
+ if (!r) {
PyErr_Clear();
- r = d;
- Py_INCREF(d);
+ return 0;
+ } else {
+ Py_DECREF(r);
+ return 1;
}
- return r;
-bad:
- return NULL;
}
//////////////////// Intern.proto ////////////////////
@@ -188,11 +189,12 @@
static PyObject* __Pyx_Intern(PyObject* s); /* proto */
//////////////////// Intern ////////////////////
+//@requires: ObjectHandling.c::RaiseUnexpectedTypeError
static PyObject* __Pyx_Intern(PyObject* s) {
- if (!(likely(PyString_CheckExact(s)))) {
- PyErr_Format(PyExc_TypeError, "Expected %.16s, got %.200s", "str", Py_TYPE(s)->tp_name);
- return 0;
+ if (unlikely(!PyString_CheckExact(s))) {
+ __Pyx_RaiseUnexpectedTypeError("str", s);
+ return NULL;
}
Py_INCREF(s);
#if PY_MAJOR_VERSION >= 3
@@ -203,46 +205,66 @@
return s;
}
-//////////////////// abs_int.proto ////////////////////
-
-static CYTHON_INLINE unsigned int __Pyx_abs_int(int x) {
- if (unlikely(x == -INT_MAX-1))
- return ((unsigned int)INT_MAX) + 1U;
- return (unsigned int) abs(x);
-}
-
-//////////////////// abs_long.proto ////////////////////
-
-static CYTHON_INLINE unsigned long __Pyx_abs_long(long x) {
- if (unlikely(x == -LONG_MAX-1))
- return ((unsigned long)LONG_MAX) + 1U;
- return (unsigned long) labs(x);
-}
-
//////////////////// abs_longlong.proto ////////////////////
-static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_abs_longlong(PY_LONG_LONG x) {
- if (unlikely(x == -PY_LLONG_MAX-1))
- return ((unsigned PY_LONG_LONG)PY_LLONG_MAX) + 1U;
+static CYTHON_INLINE PY_LONG_LONG __Pyx_abs_longlong(PY_LONG_LONG x) {
#if defined (__cplusplus) && __cplusplus >= 201103L
- return (unsigned PY_LONG_LONG) std::abs(x);
+ return std::abs(x);
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
- return (unsigned PY_LONG_LONG) llabs(x);
-#elif defined (_MSC_VER) && defined (_M_X64)
+ return llabs(x);
+#elif defined (_MSC_VER)
// abs() is defined for long, but 64-bits type on MSVC is long long.
- // Use MS-specific _abs64 instead.
- return (unsigned PY_LONG_LONG) _abs64(x);
+ // Use MS-specific _abs64() instead, which returns the original (negative) value for abs(-MAX-1)
+ return _abs64(x);
#elif defined (__GNUC__)
// gcc or clang on 64 bit windows.
- return (unsigned PY_LONG_LONG) __builtin_llabs(x);
+ return __builtin_llabs(x);
#else
if (sizeof(PY_LONG_LONG) <= sizeof(Py_ssize_t))
return __Pyx_sst_abs(x);
- return (x<0) ? (unsigned PY_LONG_LONG)-x : (unsigned PY_LONG_LONG)x;
+ return (x<0) ? -x : x;
#endif
}
+//////////////////// py_abs.proto ////////////////////
+
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *num);/*proto*/
+
+#define __Pyx_PyNumber_Absolute(x) \
+ ((likely(PyLong_CheckExact(x))) ? \
+ (likely(Py_SIZE(x) >= 0) ? (Py_INCREF(x), (x)) : __Pyx_PyLong_AbsNeg(x)) : \
+ PyNumber_Absolute(x))
+
+#else
+#define __Pyx_PyNumber_Absolute(x) PyNumber_Absolute(x)
+#endif
+
+//////////////////// py_abs ////////////////////
+
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
+ if (likely(Py_SIZE(n) == -1)) {
+ // digits are unsigned
+ return PyLong_FromLong(((PyLongObject*)n)->ob_digit[0]);
+ }
+#if CYTHON_COMPILING_IN_CPYTHON
+ {
+ PyObject *copy = _PyLong_Copy((PyLongObject*)n);
+ if (likely(copy)) {
+ // negate the size to swap the sign
+ __Pyx_SET_SIZE(copy, -Py_SIZE(copy));
+ }
+ return copy;
+ }
+#else
+ return PyNumber_Negative(n);
+#endif
+}
+#endif
+
+
//////////////////// pow2.proto ////////////////////
#define __Pyx_PyNumber_Power2(a, b) PyNumber_Power(a, b, Py_None)
@@ -281,8 +303,11 @@
#endif
} else {
// FIXME: support character buffers - but CPython doesn't support them either
+ __Pyx_TypeName c_type_name = __Pyx_PyType_GetName(Py_TYPE(c));
PyErr_Format(PyExc_TypeError,
- "ord() expected string of length 1, but %.200s found", c->ob_type->tp_name);
+ "ord() expected string of length 1, but " __Pyx_FMT_TYPENAME " found",
+ c_type_name);
+ __Pyx_DECREF_TypeName(c_type_name);
return (long)(Py_UCS4)-1;
}
PyErr_Format(PyExc_TypeError,
@@ -371,9 +396,6 @@
//////////////////// py_dict_viewkeys.proto ////////////////////
-#if PY_VERSION_HEX < 0x02070000
-#error This module uses dict views, which require Python 2.7 or later
-#endif
static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d); /*proto*/
//////////////////// py_dict_viewkeys ////////////////////
@@ -387,9 +409,6 @@
//////////////////// py_dict_viewvalues.proto ////////////////////
-#if PY_VERSION_HEX < 0x02070000
-#error This module uses dict views, which require Python 2.7 or later
-#endif
static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d); /*proto*/
//////////////////// py_dict_viewvalues ////////////////////
@@ -403,9 +422,6 @@
//////////////////// py_dict_viewitems.proto ////////////////////
-#if PY_VERSION_HEX < 0x02070000
-#error This module uses dict views, which require Python 2.7 or later
-#endif
static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d); /*proto*/
//////////////////// py_dict_viewitems ////////////////////
@@ -417,7 +433,12 @@
return CALL_UNBOUND_METHOD(PyDict_Type, "viewitems", d);
}
+
//////////////////// pyfrozenset_new.proto ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it);
+
+//////////////////// pyfrozenset_new ////////////////////
//@substitute: naming
static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) {
@@ -440,9 +461,9 @@
result = PyFrozenSet_New(it);
if (unlikely(!result))
return NULL;
- if (likely(PySet_GET_SIZE(result)))
+ if ((PY_VERSION_HEX >= 0x031000A1) || likely(PySet_GET_SIZE(result)))
return result;
- // empty frozenset is a singleton
+ // empty frozenset is a singleton (on Python <3.10)
// seems wasteful, but CPython does the same
Py_DECREF(result);
#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Capsule.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Capsule.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Capsule.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Capsule.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,20 +0,0 @@
-//////////////// Capsule.proto ////////////////
-
-/* Todo: wrap the rest of the functionality in similar functions */
-static CYTHON_INLINE PyObject *__pyx_capsule_create(void *p, const char *sig);
-
-//////////////// Capsule ////////////////
-
-static CYTHON_INLINE PyObject *
-__pyx_capsule_create(void *p, CYTHON_UNUSED const char *sig)
-{
- PyObject *cobj;
-
-#if PY_VERSION_HEX >= 0x02070000
- cobj = PyCapsule_New(p, sig, NULL);
-#else
- cobj = PyCObject_FromVoidPtr(p, NULL);
-#endif
-
- return cobj;
-}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CConvert.pyx cython-0.20.1+1~202203241016-9537/Cython/Utility/CConvert.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CConvert.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CConvert.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -6,19 +6,20 @@
PyTypeObject *Py_TYPE(obj)
bint PyMapping_Check(obj)
object PyErr_Format(exc, const char *format, ...)
+ int __Pyx_RaiseUnexpectedTypeError(const char *expected, object obj) except 0
@cname("{{funcname}}")
-cdef {{struct_name}} {{funcname}}(obj) except *:
- cdef {{struct_name}} result
+cdef {{struct_type}} {{funcname}}(obj) except *:
+ cdef {{struct_type}} result
if not PyMapping_Check(obj):
- PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name)
+ __Pyx_RaiseUnexpectedTypeError(b"a mapping", obj)
{{for member in var_entries:}}
try:
value = obj['{{member.name}}']
except KeyError:
raise ValueError("No value specified for struct attribute '{{member.name}}'")
- result.{{member.cname}} = value
+ result.{{member.name}} = value
{{endfor}}
return result
@@ -31,13 +32,14 @@
PyTypeObject *Py_TYPE(obj)
bint PyMapping_Check(obj)
object PyErr_Format(exc, const char *format, ...)
+ int __Pyx_RaiseUnexpectedTypeError(const char *expected, object obj) except 0
@cname("{{funcname}}")
-cdef {{struct_name}} {{funcname}}(obj) except *:
- cdef {{struct_name}} result
+cdef {{struct_type}} {{funcname}}(obj) except *:
+ cdef {{struct_type}} result
cdef Py_ssize_t length
if not PyMapping_Check(obj):
- PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name)
+ __Pyx_RaiseUnexpectedTypeError(b"a mapping", obj)
last_found = None
length = len(obj)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CMath.c cython-0.20.1+1~202203241016-9537/Cython/Utility/CMath.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CMath.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CMath.c 2022-03-24 10:16:46.000000000 +0000
@@ -73,8 +73,10 @@
switch (e) {
case 3:
t *= b;
+ CYTHON_FALLTHROUGH;
case 2:
t *= b;
+ CYTHON_FALLTHROUGH;
case 1:
return t;
case 0:
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CommonStructures.c cython-0.20.1+1~202203241016-9537/Cython/Utility/CommonStructures.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CommonStructures.c 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CommonStructures.c 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,142 @@
+/////////////// FetchSharedCythonModule.proto ///////
+
+static PyObject *__Pyx_FetchSharedCythonABIModule(void);
+
+/////////////// FetchSharedCythonModule ////////////
+
+static PyObject *__Pyx_FetchSharedCythonABIModule(void) {
+ PyObject *abi_module = PyImport_AddModule((char*) __PYX_ABI_MODULE_NAME);
+ if (unlikely(!abi_module)) return NULL;
+ Py_INCREF(abi_module);
+ return abi_module;
+}
+
+/////////////// FetchCommonType.proto ///////////////
+
+#if !CYTHON_USE_TYPE_SPECS
+static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type);
+#else
+static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases);
+#endif
+
+/////////////// FetchCommonType ///////////////
+//@requires:ExtensionTypes.c::FixUpExtensionType
+//@requires: FetchSharedCythonModule
+//@requires:StringTools.c::IncludeStringH
+
+static int __Pyx_VerifyCachedType(PyObject *cached_type,
+ const char *name,
+ Py_ssize_t basicsize,
+ Py_ssize_t expected_basicsize) {
+ if (!PyType_Check(cached_type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s is not a type object", name);
+ return -1;
+ }
+ if (basicsize != expected_basicsize) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s has the wrong size, try recompiling",
+ name);
+ return -1;
+ }
+ return 0;
+}
+
+#if !CYTHON_USE_TYPE_SPECS
+static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) {
+ PyObject* abi_module;
+ const char* object_name;
+ PyTypeObject *cached_type = NULL;
+
+ abi_module = __Pyx_FetchSharedCythonABIModule();
+ if (!abi_module) return NULL;
+ // get the final part of the object name (after the last dot)
+ object_name = strrchr(type->tp_name, '.');
+ object_name = object_name ? object_name+1 : type->tp_name;
+ cached_type = (PyTypeObject*) PyObject_GetAttrString(abi_module, object_name);
+ if (cached_type) {
+ if (__Pyx_VerifyCachedType(
+ (PyObject *)cached_type,
+ object_name,
+ cached_type->tp_basicsize,
+ type->tp_basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ }
+
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad;
+ PyErr_Clear();
+ if (PyType_Ready(type) < 0) goto bad;
+ if (PyObject_SetAttrString(abi_module, object_name, (PyObject *)type) < 0)
+ goto bad;
+ Py_INCREF(type);
+ cached_type = type;
+
+done:
+ Py_DECREF(abi_module);
+ // NOTE: always returns owned reference, or NULL on error
+ return cached_type;
+
+bad:
+ Py_XDECREF(cached_type);
+ cached_type = NULL;
+ goto done;
+}
+#else
+
+static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) {
+ PyObject *abi_module, *cached_type = NULL;
+ // get the final part of the object name (after the last dot)
+ const char* object_name = strrchr(spec->name, '.');
+ object_name = object_name ? object_name+1 : spec->name;
+
+ abi_module = __Pyx_FetchSharedCythonABIModule();
+ if (!abi_module) return NULL;
+
+ cached_type = PyObject_GetAttrString(abi_module, object_name);
+ if (cached_type) {
+ Py_ssize_t basicsize;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_basicsize;
+ py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__");
+ if (unlikely(!py_basicsize)) goto bad;
+ basicsize = PyLong_AsSsize_t(py_basicsize);
+ Py_DECREF(py_basicsize);
+ py_basicsize = 0;
+ if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad;
+#else
+ basicsize = likely(PyType_Check(cached_type)) ? ((PyTypeObject*) cached_type)->tp_basicsize : -1;
+#endif
+ if (__Pyx_VerifyCachedType(
+ cached_type,
+ object_name,
+ basicsize,
+ spec->basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ }
+
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad;
+ PyErr_Clear();
+ // We pass the ABI module reference to avoid keeping the user module alive by foreign type usages.
+ (void) module;
+ cached_type = __Pyx_PyType_FromModuleAndSpec(abi_module, spec, bases);
+ if (unlikely(!cached_type)) goto bad;
+ if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad;
+ if (PyObject_SetAttrString(abi_module, object_name, cached_type) < 0) goto bad;
+
+done:
+ Py_DECREF(abi_module);
+ // NOTE: always returns owned reference, or NULL on error
+ assert(cached_type == NULL || PyType_Check(cached_type));
+ return (PyTypeObject *) cached_type;
+
+bad:
+ Py_XDECREF(cached_type);
+ cached_type = NULL;
+ goto done;
+}
+#endif
+
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CommonTypes.c cython-0.20.1+1~202203241016-9537/Cython/Utility/CommonTypes.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CommonTypes.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CommonTypes.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,48 +0,0 @@
-/////////////// FetchCommonType.proto ///////////////
-
-static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type);
-
-/////////////// FetchCommonType ///////////////
-
-static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) {
- PyObject* fake_module;
- PyTypeObject* cached_type = NULL;
-
- fake_module = PyImport_AddModule((char*) "_cython_" CYTHON_ABI);
- if (!fake_module) return NULL;
- Py_INCREF(fake_module);
-
- cached_type = (PyTypeObject*) PyObject_GetAttrString(fake_module, type->tp_name);
- if (cached_type) {
- if (!PyType_Check((PyObject*)cached_type)) {
- PyErr_Format(PyExc_TypeError,
- "Shared Cython type %.200s is not a type object",
- type->tp_name);
- goto bad;
- }
- if (cached_type->tp_basicsize != type->tp_basicsize) {
- PyErr_Format(PyExc_TypeError,
- "Shared Cython type %.200s has the wrong size, try recompiling",
- type->tp_name);
- goto bad;
- }
- } else {
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad;
- PyErr_Clear();
- if (PyType_Ready(type) < 0) goto bad;
- if (PyObject_SetAttrString(fake_module, type->tp_name, (PyObject*) type) < 0)
- goto bad;
- Py_INCREF(type);
- cached_type = type;
- }
-
-done:
- Py_DECREF(fake_module);
- // NOTE: always returns owned reference, or NULL on error
- return cached_type;
-
-bad:
- Py_XDECREF(cached_type);
- cached_type = NULL;
- goto done;
-}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Complex.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Complex.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Complex.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Complex.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,4 +1,5 @@
-/////////////// Header.proto.h_code ///////////////
+/////////////// Header.proto ///////////////
+//@proto_block: h_code
#if !defined(CYTHON_CCOMPLEX)
#if defined(__cplusplus)
@@ -49,7 +50,8 @@
#endif
-/////////////// Declarations.proto.complex_type_declarations ///////////////
+/////////////// Declarations.proto ///////////////
+//@proto_block: complex_type_declarations
#if CYTHON_CCOMPLEX
#ifdef __cplusplus
@@ -186,13 +188,13 @@
return {{type_name}}_from_parts(a.real / b.real, a.imag / b.imag);
} else {
{{real_type}} r = b.imag / b.real;
- {{real_type}} s = 1.0 / (b.real + b.imag * r);
+ {{real_type}} s = ({{real_type}})(1.0) / (b.real + b.imag * r);
return {{type_name}}_from_parts(
(a.real + a.imag * r) * s, (a.imag - a.real * r) * s);
}
} else {
{{real_type}} r = b.real / b.imag;
- {{real_type}} s = 1.0 / (b.imag + b.real * r);
+ {{real_type}} s = ({{real_type}})(1.0) / (b.imag + b.real * r);
return {{type_name}}_from_parts(
(a.real * r + a.imag) * s, (a.imag * r - a.real) * s);
}
@@ -251,7 +253,6 @@
case 1:
return a;
case 2:
- z = __Pyx_c_prod{{func_suffix}}(a, a);
return __Pyx_c_prod{{func_suffix}}(a, a);
case 3:
z = __Pyx_c_prod{{func_suffix}}(a, a);
@@ -264,9 +265,17 @@
if (a.imag == 0) {
if (a.real == 0) {
return a;
+ } else if (b.imag == 0) {
+ z.real = pow{{m}}(a.real, b.real);
+ z.imag = 0;
+ return z;
+ } else if (a.real > 0) {
+ r = a.real;
+ theta = 0;
+ } else {
+ r = -a.real;
+ theta = atan2{{m}}(0.0, -1.0);
}
- r = a.real;
- theta = 0;
} else {
r = __Pyx_c_abs{{func_suffix}}(a);
theta = atan2{{m}}(a.imag, a.real);
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Coroutine.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Coroutine.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Coroutine.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Coroutine.c 2022-03-24 10:16:46.000000000 +0000
@@ -5,10 +5,20 @@
//////////////////// GeneratorYieldFrom ////////////////////
//@requires: Generator
+#if CYTHON_USE_TYPE_SLOTS
+static void __Pyx_PyIter_CheckErrorAndDecref(PyObject *source) {
+ __Pyx_TypeName source_type_name = __Pyx_PyType_GetName(Py_TYPE(source));
+ PyErr_Format(PyExc_TypeError,
+ "iter() returned non-iterator of type '" __Pyx_FMT_TYPENAME "'", source_type_name);
+ __Pyx_DECREF_TypeName(source_type_name);
+ Py_DECREF(source);
+}
+#endif
+
static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
PyObject *source_gen, *retval;
#ifdef __Pyx_Coroutine_USED
- if (__Pyx_Coroutine_CheckExact(source)) {
+ if (__Pyx_Coroutine_Check(source)) {
// TODO: this should only happen for types.coroutine()ed generators, but we can't determine that here
Py_INCREF(source);
source_gen = source;
@@ -22,17 +32,19 @@
if (unlikely(!source_gen))
return NULL;
if (unlikely(!PyIter_Check(source_gen))) {
- PyErr_Format(PyExc_TypeError,
- "iter() returned non-iterator of type '%.100s'",
- Py_TYPE(source_gen)->tp_name);
- Py_DECREF(source_gen);
+ __Pyx_PyIter_CheckErrorAndDecref(source_gen);
return NULL;
}
} else
+ // CPython also allows non-iterable sequences to be iterated over
#endif
+ {
source_gen = PyObject_GetIter(source);
+ if (unlikely(!source_gen))
+ return NULL;
+ }
// source_gen is now the iterator, make the first next() call
- retval = Py_TYPE(source_gen)->tp_iternext(source_gen);
+ retval = __Pyx_PyObject_GetIterNextFunc(source_gen)(source_gen);
}
if (likely(retval)) {
gen->yieldfrom = source_gen;
@@ -45,115 +57,55 @@
//////////////////// CoroutineYieldFrom.proto ////////////////////
-#define __Pyx_Coroutine_Yield_From(gen, source) __Pyx__Coroutine_Yield_From(gen, source, 0)
-static CYTHON_INLINE PyObject* __Pyx__Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source, int warn);
+static CYTHON_INLINE PyObject* __Pyx_Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source);
//////////////////// CoroutineYieldFrom ////////////////////
//@requires: Coroutine
//@requires: GetAwaitIter
-static int __Pyx_WarnAIterDeprecation(PyObject *aiter) {
- int result;
-#if PY_MAJOR_VERSION >= 3
- result = PyErr_WarnFormat(
- PyExc_PendingDeprecationWarning, 1,
- "'%.100s' implements legacy __aiter__ protocol; "
- "__aiter__ should return an asynchronous "
- "iterator, not awaitable",
- Py_TYPE(aiter)->tp_name);
-#else
- result = PyErr_WarnEx(
- PyExc_PendingDeprecationWarning,
- "object implements legacy __aiter__ protocol; "
- "__aiter__ should return an asynchronous "
- "iterator, not awaitable",
- 1);
-#endif
- return result != 0;
+static PyObject* __Pyx__Coroutine_Yield_From_Generic(__pyx_CoroutineObject *gen, PyObject *source) {
+ PyObject *retval;
+ PyObject *source_gen = __Pyx__Coroutine_GetAwaitableIter(source);
+ if (unlikely(!source_gen)) {
+ return NULL;
+ }
+ // source_gen is now the iterator, make the first next() call
+ if (__Pyx_Coroutine_Check(source_gen)) {
+ retval = __Pyx_Generator_Next(source_gen);
+ } else {
+ retval = __Pyx_PyObject_GetIterNextFunc(source_gen)(source_gen);
+ }
+ if (retval) {
+ gen->yieldfrom = source_gen;
+ return retval;
+ }
+ Py_DECREF(source_gen);
+ return NULL;
}
-static CYTHON_INLINE PyObject* __Pyx__Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source, int warn) {
+static CYTHON_INLINE PyObject* __Pyx_Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
PyObject *retval;
- if (__Pyx_Coroutine_CheckExact(source)) {
- if (warn && unlikely(__Pyx_WarnAIterDeprecation(source))) {
- /* Warning was converted to an error. */
+ if (__Pyx_Coroutine_Check(source)) {
+ if (unlikely(((__pyx_CoroutineObject*)source)->yieldfrom)) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "coroutine is being awaited already");
return NULL;
}
retval = __Pyx_Generator_Next(source);
- if (retval) {
- Py_INCREF(source);
- gen->yieldfrom = source;
- return retval;
- }
+#ifdef __Pyx_AsyncGen_USED
+ // inlined "__pyx_PyAsyncGenASend" handling to avoid the series of generic calls
+ } else if (__pyx_PyAsyncGenASend_CheckExact(source)) {
+ retval = __Pyx_async_gen_asend_iternext(source);
+#endif
} else {
- PyObject *source_gen = __Pyx__Coroutine_GetAwaitableIter(source);
- if (unlikely(!source_gen))
- return NULL;
- if (warn && unlikely(__Pyx_WarnAIterDeprecation(source))) {
- /* Warning was converted to an error. */
- Py_DECREF(source_gen);
- return NULL;
- }
- // source_gen is now the iterator, make the first next() call
- if (__Pyx_Coroutine_CheckExact(source_gen)) {
- retval = __Pyx_Generator_Next(source_gen);
- } else {
- retval = Py_TYPE(source_gen)->tp_iternext(source_gen);
- }
- if (retval) {
- gen->yieldfrom = source_gen;
- return retval;
- }
- Py_DECREF(source_gen);
- }
- return NULL;
-}
-
-
-//////////////////// CoroutineAIterYieldFrom.proto ////////////////////
-
-static CYTHON_INLINE PyObject* __Pyx_Coroutine_AIter_Yield_From(__pyx_CoroutineObject *gen, PyObject *source);
-
-//////////////////// CoroutineAIterYieldFrom ////////////////////
-//@requires: CoroutineYieldFrom
-
-static CYTHON_INLINE PyObject* __Pyx_Coroutine_AIter_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
-#if CYTHON_USE_ASYNC_SLOTS
- __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(source);
- if (likely(am && am->am_anext)) {
- // Starting with CPython 3.5.2, __aiter__ should return
- // asynchronous iterators directly (not awaitables that
- // resolve to asynchronous iterators.)
- //
- // Therefore, we check if the object that was returned
- // from __aiter__ has an __anext__ method. If it does,
- // we return it directly as StopIteration result,
- // which avoids yielding.
- //
- // See http://bugs.python.org/issue27243 for more
- // details.
- PyErr_SetObject(PyExc_StopIteration, source);
- return NULL;
+ return __Pyx__Coroutine_Yield_From_Generic(gen, source);
}
-#endif
-#if PY_VERSION_HEX < 0x030500B2
- if (!__Pyx_PyType_AsAsync(source)) {
- #ifdef __Pyx_Coroutine_USED
- if (!__Pyx_Coroutine_CheckExact(source)) // quickly rule out a likely case
- #endif
- {
- // same as above in slow
- PyObject *method = __Pyx_PyObject_GetAttrStr(source, PYIDENT("__anext__"));
- if (method) {
- Py_DECREF(method);
- PyErr_SetObject(PyExc_StopIteration, source);
- return NULL;
- }
- PyErr_Clear();
- }
+ if (retval) {
+ Py_INCREF(source);
+ gen->yieldfrom = source;
}
-#endif
- return __Pyx__Coroutine_Yield_From(gen, source, 1);
+ return retval;
}
@@ -163,20 +115,54 @@
static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *o); /*proto*/
//////////////////// GetAwaitIter ////////////////////
-//@requires: ObjectHandling.c::PyObjectGetAttrStr
+//@requires: ObjectHandling.c::PyObjectGetMethod
//@requires: ObjectHandling.c::PyObjectCallNoArg
//@requires: ObjectHandling.c::PyObjectCallOneArg
static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAwaitableIter(PyObject *o) {
#ifdef __Pyx_Coroutine_USED
- if (__Pyx_Coroutine_CheckExact(o)) {
- Py_INCREF(o);
- return o;
+ if (__Pyx_Coroutine_Check(o)) {
+ return __Pyx_NewRef(o);
}
#endif
return __Pyx__Coroutine_GetAwaitableIter(o);
}
+
+static void __Pyx_Coroutine_AwaitableIterError(PyObject *source) {
+#if PY_VERSION_HEX >= 0x030600B3 || defined(_PyErr_FormatFromCause)
+ __Pyx_TypeName source_type_name = __Pyx_PyType_GetName(Py_TYPE(source));
+ _PyErr_FormatFromCause(PyExc_TypeError,
+ "'async for' received an invalid object from __anext__: " __Pyx_FMT_TYPENAME, source_type_name);
+ __Pyx_DECREF_TypeName(source_type_name);
+#elif PY_MAJOR_VERSION >= 3
+ PyObject *exc, *val, *val2, *tb;
+ __Pyx_TypeName source_type_name = __Pyx_PyType_GetName(Py_TYPE(source));
+ assert(PyErr_Occurred());
+ PyErr_Fetch(&exc, &val, &tb);
+ PyErr_NormalizeException(&exc, &val, &tb);
+ if (tb != NULL) {
+ PyException_SetTraceback(val, tb);
+ Py_DECREF(tb);
+ }
+ Py_DECREF(exc);
+ assert(!PyErr_Occurred());
+ PyErr_Format(PyExc_TypeError,
+ "'async for' received an invalid object from __anext__: " __Pyx_FMT_TYPENAME, source_type_name);
+ __Pyx_DECREF_TypeName(source_type_name);
+
+ PyErr_Fetch(&exc, &val2, &tb);
+ PyErr_NormalizeException(&exc, &val2, &tb);
+ Py_INCREF(val);
+ PyException_SetCause(val2, val);
+ PyException_SetContext(val2, val);
+ PyErr_Restore(exc, val2, tb);
+#else
+ // since Py2 does not have exception chaining, it's better to avoid shadowing exceptions there
+ source++;
+#endif
+}
+
// adapted from genobject.c in Py3.5
static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *obj) {
PyObject *res;
@@ -188,43 +174,41 @@
#endif
#if PY_VERSION_HEX >= 0x030500B2 || defined(PyCoro_CheckExact)
if (PyCoro_CheckExact(obj)) {
- Py_INCREF(obj);
- return obj;
+ return __Pyx_NewRef(obj);
} else
#endif
#if CYTHON_COMPILING_IN_CPYTHON && defined(CO_ITERABLE_COROUTINE)
if (PyGen_CheckExact(obj) && ((PyGenObject*)obj)->gi_code && ((PyCodeObject *)((PyGenObject*)obj)->gi_code)->co_flags & CO_ITERABLE_COROUTINE) {
// Python generator marked with "@types.coroutine" decorator
- Py_INCREF(obj);
- return obj;
+ return __Pyx_NewRef(obj);
} else
#endif
{
- PyObject *method = __Pyx_PyObject_GetAttrStr(obj, PYIDENT("__await__"));
- if (unlikely(!method)) goto slot_error;
- #if CYTHON_UNPACK_METHODS
- if (likely(PyMethod_Check(method))) {
- PyObject *self = PyMethod_GET_SELF(method);
- if (likely(self)) {
- PyObject *function = PyMethod_GET_FUNCTION(method);
- res = __Pyx_PyObject_CallOneArg(function, self);
- } else
- res = __Pyx_PyObject_CallNoArg(method);
- } else
- #endif
+ PyObject *method = NULL;
+ int is_method = __Pyx_PyObject_GetMethod(obj, PYIDENT("__await__"), &method);
+ if (likely(is_method)) {
+ res = __Pyx_PyObject_CallOneArg(method, obj);
+ } else if (likely(method)) {
res = __Pyx_PyObject_CallNoArg(method);
+ } else
+ goto slot_error;
Py_DECREF(method);
}
- if (unlikely(!res)) goto bad;
- if (!PyIter_Check(res)) {
+ if (unlikely(!res)) {
+ // surprisingly, CPython replaces the exception here...
+ __Pyx_Coroutine_AwaitableIterError(obj);
+ goto bad;
+ }
+ if (unlikely(!PyIter_Check(res))) {
+ __Pyx_TypeName res_type_name = __Pyx_PyType_GetName(Py_TYPE(res));
PyErr_Format(PyExc_TypeError,
- "__await__() returned non-iterator of type '%.100s'",
- Py_TYPE(res)->tp_name);
+ "__await__() returned non-iterator of type '" __Pyx_FMT_TYPENAME "'", res_type_name);
+ __Pyx_DECREF_TypeName(res_type_name);
Py_CLEAR(res);
} else {
int is_coroutine = 0;
#ifdef __Pyx_Coroutine_USED
- is_coroutine |= __Pyx_Coroutine_CheckExact(res);
+ is_coroutine |= __Pyx_Coroutine_Check(res);
#endif
#if PY_VERSION_HEX >= 0x030500B2 || defined(PyCoro_CheckExact)
is_coroutine |= PyCoro_CheckExact(res);
@@ -239,9 +223,12 @@
}
return res;
slot_error:
- PyErr_Format(PyExc_TypeError,
- "object %.100s can't be used in 'await' expression",
- Py_TYPE(obj)->tp_name);
+ {
+ __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "object " __Pyx_FMT_TYPENAME " can't be used in 'await' expression", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ }
bad:
return NULL;
}
@@ -256,13 +243,8 @@
//@requires: GetAwaitIter
//@requires: ObjectHandling.c::PyObjectCallMethod0
-static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) {
-#if CYTHON_USE_ASYNC_SLOTS
- __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
- if (likely(am && am->am_aiter)) {
- return (*am->am_aiter)(obj);
- }
-#endif
+static PyObject *__Pyx_Coroutine_GetAsyncIter_Generic(PyObject *obj) {
+ __Pyx_TypeName obj_type_name;
#if PY_VERSION_HEX < 0x030500B1
{
PyObject *iter = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__"));
@@ -274,21 +256,36 @@
}
#else
// avoid C warning about 'unused function'
- if (0) (void) __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__"));
+ if ((0)) (void) __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__"));
#endif
- PyErr_Format(PyExc_TypeError, "'async for' requires an object with __aiter__ method, got %.100s",
- Py_TYPE(obj)->tp_name);
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'async for' requires an object with __aiter__ method, got " __Pyx_FMT_TYPENAME, obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
return NULL;
}
-static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *obj) {
+
+static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) {
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(obj)) {
+ return __Pyx_NewRef(obj);
+ }
+#endif
#if CYTHON_USE_ASYNC_SLOTS
- __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
- if (likely(am && am->am_anext)) {
- return (*am->am_anext)(obj);
+ {
+ __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
+ if (likely(am && am->am_aiter)) {
+ return (*am->am_aiter)(obj);
+ }
}
#endif
+ return __Pyx_Coroutine_GetAsyncIter_Generic(obj);
+}
+
+
+static PyObject *__Pyx__Coroutine_AsyncIterNext(PyObject *obj) {
#if PY_VERSION_HEX < 0x030500B1
{
PyObject *value = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__anext__"));
@@ -298,92 +295,192 @@
// FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__anext__'
if (PyErr_ExceptionMatches(PyExc_AttributeError))
#endif
- PyErr_Format(PyExc_TypeError, "'async for' requires an object with __anext__ method, got %.100s",
- Py_TYPE(obj)->tp_name);
+ {
+ __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'async for' requires an object with __anext__ method, got " __Pyx_FMT_TYPENAME, obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ }
return NULL;
}
+static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *obj) {
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(obj)) {
+ return __Pyx_async_gen_anext(obj);
+ }
+#endif
+#if CYTHON_USE_ASYNC_SLOTS
+ {
+ __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
+ if (likely(am && am->am_anext)) {
+ return (*am->am_anext)(obj);
+ }
+ }
+#endif
+ return __Pyx__Coroutine_AsyncIterNext(obj);
+}
+
+
//////////////////// pep479.proto ////////////////////
-static void __Pyx_Generator_Replace_StopIteration(void); /*proto*/
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen); /*proto*/
//////////////////// pep479 ////////////////////
//@requires: Exceptions.c::GetException
-static void __Pyx_Generator_Replace_StopIteration(void) {
- PyObject *exc, *val, *tb;
- // Chain exceptions by moving StopIteration to exc_info before creating the RuntimeError.
- // In Py2.x, no chaining happens, but the exception still stays visible in exc_info.
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen) {
+ PyObject *exc, *val, *tb, *cur_exc;
__Pyx_PyThreadState_declare
+ #ifdef __Pyx_StopAsyncIteration_USED
+ int is_async_stopiteration = 0;
+ #endif
+ CYTHON_MAYBE_UNUSED_VAR(in_async_gen);
+
+ cur_exc = PyErr_Occurred();
+ if (likely(!__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopIteration))) {
+ #ifdef __Pyx_StopAsyncIteration_USED
+ if (in_async_gen && unlikely(__Pyx_PyErr_GivenExceptionMatches(cur_exc, __Pyx_PyExc_StopAsyncIteration))) {
+ is_async_stopiteration = 1;
+ } else
+ #endif
+ return;
+ }
+
__Pyx_PyThreadState_assign
+ // Chain exceptions by moving Stop(Async)Iteration to exc_info before creating the RuntimeError.
+ // In Py2.x, no chaining happens, but the exception still stays visible in exc_info.
__Pyx_GetException(&exc, &val, &tb);
Py_XDECREF(exc);
Py_XDECREF(val);
Py_XDECREF(tb);
- PyErr_SetString(PyExc_RuntimeError, "generator raised StopIteration");
+ PyErr_SetString(PyExc_RuntimeError,
+ #ifdef __Pyx_StopAsyncIteration_USED
+ is_async_stopiteration ? "async generator raised StopAsyncIteration" :
+ in_async_gen ? "async generator raised StopIteration" :
+ #endif
+ "generator raised StopIteration");
}
//////////////////// CoroutineBase.proto ////////////////////
+//@substitute: naming
-typedef PyObject *(*__pyx_coroutine_body_t)(PyObject *, PyObject *);
+struct __pyx_CoroutineObject;
+typedef PyObject *(*__pyx_coroutine_body_t)(struct __pyx_CoroutineObject *, PyThreadState *, PyObject *);
+#if CYTHON_USE_EXC_INFO_STACK
+// See https://bugs.python.org/issue25612
+#define __Pyx_ExcInfoStruct _PyErr_StackItem
+#else
+// Minimal replacement struct for Py<3.7, without the Py3.7 exception state stack.
typedef struct {
- PyObject_HEAD
- __pyx_coroutine_body_t body;
- PyObject *closure;
PyObject *exc_type;
PyObject *exc_value;
PyObject *exc_traceback;
+} __Pyx_ExcInfoStruct;
+#endif
+
+typedef struct __pyx_CoroutineObject {
+ PyObject_HEAD
+ __pyx_coroutine_body_t body;
+ PyObject *closure;
+ __Pyx_ExcInfoStruct gi_exc_state;
PyObject *gi_weakreflist;
PyObject *classobj;
PyObject *yieldfrom;
PyObject *gi_name;
PyObject *gi_qualname;
PyObject *gi_modulename;
+ PyObject *gi_code;
+ PyObject *gi_frame;
int resume_label;
// using T_BOOL for property below requires char value
char is_running;
} __pyx_CoroutineObject;
static __pyx_CoroutineObject *__Pyx__Coroutine_New(
- PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *closure,
+ PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
PyObject *name, PyObject *qualname, PyObject *module_name); /*proto*/
+
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name); /*proto*/
+
+static CYTHON_INLINE void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *self);
static int __Pyx_Coroutine_clear(PyObject *self); /*proto*/
+static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value); /*proto*/
+static PyObject *__Pyx_Coroutine_Close(PyObject *self); /*proto*/
+static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args); /*proto*/
-#if 1 || PY_VERSION_HEX < 0x030300B0
-static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue); /*proto*/
+// macros for exception state swapping instead of inline functions to make use of the local thread state context
+#if CYTHON_USE_EXC_INFO_STACK
+#define __Pyx_Coroutine_SwapException(self)
+#define __Pyx_Coroutine_ResetAndClearException(self) __Pyx_Coroutine_ExceptionClear(&(self)->gi_exc_state)
#else
-#define __Pyx_PyGen_FetchStopIterationValue(pvalue) PyGen_FetchStopIterationValue(pvalue)
+#define __Pyx_Coroutine_SwapException(self) { \
+ __Pyx_ExceptionSwap(&(self)->gi_exc_state.exc_type, &(self)->gi_exc_state.exc_value, &(self)->gi_exc_state.exc_traceback); \
+ __Pyx_Coroutine_ResetFrameBackpointer(&(self)->gi_exc_state); \
+ }
+#define __Pyx_Coroutine_ResetAndClearException(self) { \
+ __Pyx_ExceptionReset((self)->gi_exc_state.exc_type, (self)->gi_exc_state.exc_value, (self)->gi_exc_state.exc_traceback); \
+ (self)->gi_exc_state.exc_type = (self)->gi_exc_state.exc_value = (self)->gi_exc_state.exc_traceback = NULL; \
+ }
#endif
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue) \
+ __Pyx_PyGen__FetchStopIterationValue($local_tstate_cname, pvalue)
+#else
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue) \
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue)
+#endif
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue); /*proto*/
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state); /*proto*/
+
//////////////////// Coroutine.proto ////////////////////
#define __Pyx_Coroutine_USED
static PyTypeObject *__pyx_CoroutineType = 0;
static PyTypeObject *__pyx_CoroutineAwaitType = 0;
-#define __Pyx_Coroutine_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineType)
+#define __Pyx_Coroutine_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CoroutineType)
+// __Pyx_Coroutine_Check(obj): see override for IterableCoroutine below
+#define __Pyx_Coroutine_Check(obj) __Pyx_Coroutine_CheckExact(obj)
+#define __Pyx_CoroutineAwait_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CoroutineAwaitType)
-#define __Pyx_Coroutine_New(body, closure, name, qualname, module_name) \
- __Pyx__Coroutine_New(__pyx_CoroutineType, body, closure, name, qualname, module_name)
+#define __Pyx_Coroutine_New(body, code, closure, name, qualname, module_name) \
+ __Pyx__Coroutine_New(__pyx_CoroutineType, body, code, closure, name, qualname, module_name)
-static int __pyx_Coroutine_init(void); /*proto*/
+static int __pyx_Coroutine_init(PyObject *module); /*proto*/
static PyObject *__Pyx__Coroutine_await(PyObject *coroutine); /*proto*/
+typedef struct {
+ PyObject_HEAD
+ PyObject *coroutine;
+} __pyx_CoroutineAwaitObject;
+
+static PyObject *__Pyx_CoroutineAwait_Close(__pyx_CoroutineAwaitObject *self, PyObject *arg); /*proto*/
+static PyObject *__Pyx_CoroutineAwait_Throw(__pyx_CoroutineAwaitObject *self, PyObject *args); /*proto*/
+
//////////////////// Generator.proto ////////////////////
#define __Pyx_Generator_USED
static PyTypeObject *__pyx_GeneratorType = 0;
-#define __Pyx_Generator_CheckExact(obj) (Py_TYPE(obj) == __pyx_GeneratorType)
+#define __Pyx_Generator_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_GeneratorType)
-#define __Pyx_Generator_New(body, closure, name, qualname, module_name) \
- __Pyx__Coroutine_New(__pyx_GeneratorType, body, closure, name, qualname, module_name)
+#define __Pyx_Generator_New(body, code, closure, name, qualname, module_name) \
+ __Pyx__Coroutine_New(__pyx_GeneratorType, body, code, closure, name, qualname, module_name)
static PyObject *__Pyx_Generator_Next(PyObject *self);
-static int __pyx_Generator_init(void); /*proto*/
+static int __pyx_Generator_init(PyObject *module); /*proto*/
+
+
+//////////////////// AsyncGen ////////////////////
+//@requires: AsyncGen.c::AsyncGenerator
+// -> empty, only delegates to separate file
//////////////////// CoroutineBase ////////////////////
@@ -392,16 +489,22 @@
//@requires: Exceptions.c::PyThreadStateGet
//@requires: Exceptions.c::SwapException
//@requires: Exceptions.c::RaiseException
+//@requires: Exceptions.c::SaveResetException
//@requires: ObjectHandling.c::PyObjectCallMethod1
+//@requires: ObjectHandling.c::PyObjectCallNoArg
+//@requires: ObjectHandling.c::PyObjectFastCall
//@requires: ObjectHandling.c::PyObjectGetAttrStr
-//@requires: CommonTypes.c::FetchCommonType
+//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError
+//@requires: CommonStructures.c::FetchCommonType
+//@requires: ModuleSetupCode.c::IncludeStructmemberH
-#include
#include
-
-static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value);
-static PyObject *__Pyx_Coroutine_Close(PyObject *self);
-static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args);
+#if PY_VERSION_HEX >= 0x030b00a6
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
#define __Pyx_Coroutine_Undelegate(gen) Py_CLEAR((gen)->yieldfrom)
@@ -411,12 +514,10 @@
// Returns 0 if no exception or StopIteration is set.
// If any other exception is set, returns -1 and leaves
// pvalue unchanged.
-#if 1 || PY_VERSION_HEX < 0x030300B0
-static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) {
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *$local_tstate_cname, PyObject **pvalue) {
PyObject *et, *ev, *tb;
PyObject *value = NULL;
- __Pyx_PyThreadState_declare
- __Pyx_PyThreadState_assign
+ CYTHON_UNUSED_VAR($local_tstate_cname);
__Pyx_ErrFetch(&et, &ev, &tb);
@@ -435,7 +536,7 @@
value = Py_None;
}
#if PY_VERSION_HEX >= 0x030300A0
- else if (Py_TYPE(ev) == (PyTypeObject*)PyExc_StopIteration) {
+ else if (likely(__Pyx_IS_TYPE(ev, (PyTypeObject*)PyExc_StopIteration))) {
value = ((PyStopIterationObject *)ev)->value;
Py_INCREF(value);
Py_DECREF(ev);
@@ -457,7 +558,7 @@
}
Py_DECREF(ev);
}
- else if (!PyObject_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) {
+ else if (!__Pyx_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) {
// 'steal' reference to ev
value = ev;
}
@@ -467,7 +568,7 @@
*pvalue = value;
return 0;
}
- } else if (!PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) {
+ } else if (!__Pyx_PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) {
__Pyx_ErrRestore(et, ev, tb);
return -1;
}
@@ -503,119 +604,277 @@
*pvalue = value;
return 0;
}
-#endif
static CYTHON_INLINE
-void __Pyx_Coroutine_ExceptionClear(__pyx_CoroutineObject *self) {
- PyObject *exc_type = self->exc_type;
- PyObject *exc_value = self->exc_value;
- PyObject *exc_traceback = self->exc_traceback;
-
- self->exc_type = NULL;
- self->exc_value = NULL;
- self->exc_traceback = NULL;
-
- Py_XDECREF(exc_type);
- Py_XDECREF(exc_value);
- Py_XDECREF(exc_traceback);
+void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *exc_state) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_CLEAR(exc_state->exc_value);
+#else
+ PyObject *t, *v, *tb;
+ t = exc_state->exc_type;
+ v = exc_state->exc_value;
+ tb = exc_state->exc_traceback;
+
+ exc_state->exc_type = NULL;
+ exc_state->exc_value = NULL;
+ exc_state->exc_traceback = NULL;
+
+ Py_XDECREF(t);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+#endif
}
-static CYTHON_INLINE
-int __Pyx_Coroutine_CheckRunning(__pyx_CoroutineObject *gen) {
- if (unlikely(gen->is_running)) {
- PyErr_SetString(PyExc_ValueError,
- "generator already executing");
- return 1;
+#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL)
+static void __Pyx__Coroutine_AlreadyRunningError(__pyx_CoroutineObject *gen) {
+ const char *msg;
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check((PyObject*)gen)) {
+ msg = "coroutine already executing";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact((PyObject*)gen)) {
+ msg = "async generator already executing";
+ #endif
+ } else {
+ msg = "generator already executing";
}
- return 0;
+ PyErr_SetString(PyExc_ValueError, msg);
}
-static CYTHON_INLINE
-PyObject *__Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value) {
- PyObject *retval;
+#define __Pyx_Coroutine_NotStartedError(gen) (__Pyx__Coroutine_NotStartedError(gen), (PyObject*)NULL)
+static void __Pyx__Coroutine_NotStartedError(PyObject *gen) {
+ const char *msg;
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check(gen)) {
+ msg = "can't send non-None value to a just-started coroutine";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact(gen)) {
+ msg = "can't send non-None value to a just-started async generator";
+ #endif
+ } else {
+ msg = "can't send non-None value to a just-started generator";
+ }
+ PyErr_SetString(PyExc_TypeError, msg);
+}
+
+#define __Pyx_Coroutine_AlreadyTerminatedError(gen, value, closing) (__Pyx__Coroutine_AlreadyTerminatedError(gen, value, closing), (PyObject*)NULL)
+static void __Pyx__Coroutine_AlreadyTerminatedError(PyObject *gen, PyObject *value, int closing) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ CYTHON_MAYBE_UNUSED_VAR(closing);
+ #ifdef __Pyx_Coroutine_USED
+ if (!closing && __Pyx_Coroutine_Check(gen)) {
+ // `self` is an exhausted coroutine: raise an error,
+ // except when called from gen_close(), which should
+ // always be a silent method.
+ PyErr_SetString(PyExc_RuntimeError, "cannot reuse already awaited coroutine");
+ } else
+ #endif
+ if (value) {
+ // `gen` is an exhausted generator:
+ // only set exception if called from send().
+ #ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(gen))
+ PyErr_SetNone(__Pyx_PyExc_StopAsyncIteration);
+ else
+ #endif
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+}
+
+static
+PyObject *__Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, int closing) {
__Pyx_PyThreadState_declare
+ PyThreadState *tstate;
+ __Pyx_ExcInfoStruct *exc_state;
+ PyObject *retval;
assert(!self->is_running);
if (unlikely(self->resume_label == 0)) {
if (unlikely(value && value != Py_None)) {
- PyErr_SetString(PyExc_TypeError,
- "can't send non-None value to a "
- "just-started generator");
- return NULL;
+ return __Pyx_Coroutine_NotStartedError((PyObject*)self);
}
}
if (unlikely(self->resume_label == -1)) {
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+ return __Pyx_Coroutine_AlreadyTerminatedError((PyObject*)self, value, closing);
}
+#if CYTHON_FAST_THREAD_STATE
__Pyx_PyThreadState_assign
- if (value) {
-#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_PYSTON
- // FIXME: what to do in PyPy?
+ tstate = $local_tstate_cname;
#else
+ tstate = __Pyx_PyThreadState_Current;
+#endif
+
+ // Traceback/Frame rules pre-Py3.7:
+ // - on entry, save external exception state in self->gi_exc_state, restore it on exit
+ // - on exit, keep internally generated exceptions in self->gi_exc_state, clear everything else
+ // - on entry, set "f_back" pointer of internal exception traceback to (current) outer call frame
+ // - on exit, clear "f_back" of internal exception traceback
+ // - do not touch external frames and tracebacks
+
+ // Traceback/Frame rules for Py3.7+ (CYTHON_USE_EXC_INFO_STACK):
+ // - on entry, push internal exception state in self->gi_exc_state on the exception stack
+ // - on exit, keep internally generated exceptions in self->gi_exc_state, clear everything else
+ // - on entry, set "f_back" pointer of internal exception traceback to (current) outer call frame
+ // - on exit, clear "f_back" of internal exception traceback
+ // - do not touch external frames and tracebacks
+
+ exc_state = &self->gi_exc_state;
+ if (exc_state->exc_value) {
+ #if CYTHON_COMPILING_IN_PYPY
+ // FIXME: what to do in PyPy?
+ #else
// Generators always return to their most recent caller, not
// necessarily their creator.
- if (self->exc_traceback) {
- PyTracebackObject *tb = (PyTracebackObject *) self->exc_traceback;
+ PyObject *exc_tb;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ // owned reference!
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+ if (exc_tb) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
PyFrameObject *f = tb->tb_frame;
- Py_XINCREF($local_tstate_cname->frame);
assert(f->f_back == NULL);
- f->f_back = $local_tstate_cname->frame;
+ #if PY_VERSION_HEX >= 0x030B00A1
+ // PyThreadState_GetFrame returns NULL if there isn't a current frame
+ // which is a valid state so no need to check
+ f->f_back = PyThreadState_GetFrame(tstate);
+ #else
+ Py_XINCREF(tstate->frame);
+ f->f_back = tstate->frame;
+ #endif
+ #if PY_VERSION_HEX >= 0x030B00a4
+ Py_DECREF(exc_tb);
+ #endif
}
-#endif
- __Pyx_ExceptionSwap(&self->exc_type, &self->exc_value,
- &self->exc_traceback);
+ #endif
+ }
+
+#if CYTHON_USE_EXC_INFO_STACK
+ // See https://bugs.python.org/issue25612
+ exc_state->previous_item = tstate->exc_info;
+ tstate->exc_info = exc_state;
+#else
+ if (exc_state->exc_type) {
+ // We were in an except handler when we left,
+ // restore the exception state which was put aside.
+ __Pyx_ExceptionSwap(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
+ // self->exc_* now holds the exception state of the caller
} else {
- __Pyx_Coroutine_ExceptionClear(self);
+ // save away the exception state of the caller
+ __Pyx_Coroutine_ExceptionClear(exc_state);
+ __Pyx_ExceptionSave(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
}
+#endif
self->is_running = 1;
- retval = self->body((PyObject *) self, value);
+ retval = self->body(self, tstate, value);
self->is_running = 0;
- if (retval) {
- __Pyx_ExceptionSwap(&self->exc_type, &self->exc_value,
- &self->exc_traceback);
-#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_PYSTON
- // FIXME: what to do in PyPy?
-#else
- // Don't keep the reference to f_back any longer than necessary. It
- // may keep a chain of frames alive or it could create a reference
- // cycle.
- if (self->exc_traceback) {
- PyTracebackObject *tb = (PyTracebackObject *) self->exc_traceback;
- PyFrameObject *f = tb->tb_frame;
- Py_CLEAR(f->f_back);
- }
+#if CYTHON_USE_EXC_INFO_STACK
+ // See https://bugs.python.org/issue25612
+ exc_state = &self->gi_exc_state;
+ tstate->exc_info = exc_state->previous_item;
+ exc_state->previous_item = NULL;
+ // Cut off the exception frame chain so that we can reconnect it on re-entry above.
+ __Pyx_Coroutine_ResetFrameBackpointer(exc_state);
#endif
- } else {
- __Pyx_Coroutine_ExceptionClear(self);
- }
return retval;
}
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state) {
+ // Don't keep the reference to f_back any longer than necessary. It
+ // may keep a chain of frames alive or it could create a reference
+ // cycle.
+#if CYTHON_COMPILING_IN_PYPY
+ // FIXME: what to do in PyPy?
+#else
+ PyObject *exc_tb;
+
+ #if PY_VERSION_HEX >= 0x030B00a4
+ if (!exc_state->exc_value) return;
+ // owned reference!
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+
+ if (likely(exc_tb)) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
+ PyFrameObject *f = tb->tb_frame;
+ Py_CLEAR(f->f_back);
+ #if PY_VERSION_HEX >= 0x030B00a4
+ Py_DECREF(exc_tb);
+ #endif
+ }
+#endif
+}
+
static CYTHON_INLINE
-PyObject *__Pyx_Coroutine_MethodReturn(PyObject *retval) {
- if (unlikely(!retval && !PyErr_Occurred())) {
- // method call must not terminate with NULL without setting an exception
- PyErr_SetNone(PyExc_StopIteration);
+PyObject *__Pyx_Coroutine_MethodReturn(PyObject* gen, PyObject *retval) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if (unlikely(!retval)) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ if (!__Pyx_PyErr_Occurred()) {
+ // method call must not terminate with NULL without setting an exception
+ PyObject *exc = PyExc_StopIteration;
+ #ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(gen))
+ exc = __Pyx_PyExc_StopAsyncIteration;
+ #endif
+ __Pyx_PyErr_SetNone(exc);
+ }
}
return retval;
}
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
+static CYTHON_INLINE
+PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) {
+#if PY_VERSION_HEX <= 0x030A00A1
+ return _PyGen_Send(gen, arg);
+#else
+ PyObject *result;
+ // PyIter_Send() asserts non-NULL arg
+ if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) {
+ if (PyAsyncGen_CheckExact(gen)) {
+ assert(result == Py_None);
+ PyErr_SetNone(PyExc_StopAsyncIteration);
+ }
+ else if (result == Py_None) {
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+ else {
+ _PyGen_SetStopIterationValue(result);
+ }
+ Py_CLEAR(result);
+ }
+ return result;
+#endif
+}
+#endif
+
static CYTHON_INLINE
PyObject *__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen) {
PyObject *ret;
PyObject *val = NULL;
__Pyx_Coroutine_Undelegate(gen);
- __Pyx_PyGen_FetchStopIterationValue(&val);
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, &val);
// val == NULL on failure => pass on exception
- ret = __Pyx_Coroutine_SendEx(gen, val);
+ ret = __Pyx_Coroutine_SendEx(gen, val, 0);
Py_XDECREF(val);
return ret;
}
@@ -624,8 +883,8 @@
PyObject *retval;
__pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
PyObject *yf = gen->yieldfrom;
- if (unlikely(__Pyx_Coroutine_CheckRunning(gen)))
- return NULL;
+ if (unlikely(gen->is_running))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
if (yf) {
PyObject *ret;
// FIXME: does this really need an INCREF() ?
@@ -637,13 +896,30 @@
} else
#endif
#ifdef __Pyx_Coroutine_USED
- if (__Pyx_Coroutine_CheckExact(yf)) {
+ if (__Pyx_Coroutine_Check(yf)) {
ret = __Pyx_Coroutine_Send(yf, value);
} else
#endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ ret = __Pyx_async_gen_asend_send(yf, value);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
+ // _PyGen_Send() is not exported before Py3.6
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03050000 && defined(PyCoro_CheckExact) && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
+ // _PyGen_Send() is not exported before Py3.6
+ if (PyCoro_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ #endif
{
if (value == Py_None)
- ret = Py_TYPE(yf)->tp_iternext(yf);
+ ret = __Pyx_PyObject_GetIterNextFunc(yf)(yf);
else
ret = __Pyx_PyObject_CallMethod1(yf, PYIDENT("send"), value);
}
@@ -654,9 +930,9 @@
}
retval = __Pyx_Coroutine_FinishDelegation(gen);
} else {
- retval = __Pyx_Coroutine_SendEx(gen, value);
+ retval = __Pyx_Coroutine_SendEx(gen, value, 0);
}
- return __Pyx_Coroutine_MethodReturn(retval);
+ return __Pyx_Coroutine_MethodReturn(self, retval);
}
// This helper function is used by gen_close and gen_throw to
@@ -673,25 +949,39 @@
} else
#endif
#ifdef __Pyx_Coroutine_USED
- if (__Pyx_Coroutine_CheckExact(yf)) {
+ if (__Pyx_Coroutine_Check(yf)) {
retval = __Pyx_Coroutine_Close(yf);
if (!retval)
return -1;
} else
+ if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ retval = __Pyx_CoroutineAwait_Close((__pyx_CoroutineAwaitObject*)yf, NULL);
+ if (!retval)
+ return -1;
+ } else
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ retval = __Pyx_async_gen_asend_close(yf, NULL);
+ // cannot fail
+ } else
+ if (__pyx_PyAsyncGenAThrow_CheckExact(yf)) {
+ retval = __Pyx_async_gen_athrow_close(yf, NULL);
+ // cannot fail
+ } else
#endif
{
PyObject *meth;
gen->is_running = 1;
- meth = __Pyx_PyObject_GetAttrStr(yf, PYIDENT("close"));
+ meth = __Pyx_PyObject_GetAttrStrNoError(yf, PYIDENT("close"));
if (unlikely(!meth)) {
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ if (unlikely(PyErr_Occurred())) {
PyErr_WriteUnraisable(yf);
}
- PyErr_Clear();
} else {
- retval = PyObject_CallFunction(meth, NULL);
+ retval = __Pyx_PyObject_CallNoArg(meth);
Py_DECREF(meth);
- if (!retval)
+ if (unlikely(!retval))
err = -1;
}
gen->is_running = 0;
@@ -703,8 +993,8 @@
static PyObject *__Pyx_Generator_Next(PyObject *self) {
__pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
PyObject *yf = gen->yieldfrom;
- if (unlikely(__Pyx_Coroutine_CheckRunning(gen)))
- return NULL;
+ if (unlikely(gen->is_running))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
if (yf) {
PyObject *ret;
// FIXME: does this really need an INCREF() ?
@@ -716,7 +1006,18 @@
ret = __Pyx_Generator_Next(yf);
} else
#endif
- ret = Py_TYPE(yf)->tp_iternext(yf);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
+ // _PyGen_Send() is not exported before Py3.6
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, Py_None);
+ } else
+ #endif
+ ret = __Pyx_PyObject_GetIterNextFunc(yf)(yf);
gen->is_running = 0;
//Py_DECREF(yf);
if (likely(ret)) {
@@ -724,7 +1025,12 @@
}
return __Pyx_Coroutine_FinishDelegation(gen);
}
- return __Pyx_Coroutine_SendEx(gen, Py_None);
+ return __Pyx_Coroutine_SendEx(gen, Py_None, 0);
+}
+
+static PyObject *__Pyx_Coroutine_Close_Method(PyObject *self, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ return __Pyx_Coroutine_Close(self);
}
static PyObject *__Pyx_Coroutine_Close(PyObject *self) {
@@ -733,8 +1039,8 @@
PyObject *yf = gen->yieldfrom;
int err = 0;
- if (unlikely(__Pyx_Coroutine_CheckRunning(gen)))
- return NULL;
+ if (unlikely(gen->is_running))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
if (yf) {
Py_INCREF(yf);
@@ -744,20 +1050,31 @@
}
if (err == 0)
PyErr_SetNone(PyExc_GeneratorExit);
- retval = __Pyx_Coroutine_SendEx(gen, NULL);
- if (retval) {
+ retval = __Pyx_Coroutine_SendEx(gen, NULL, 1);
+ if (unlikely(retval)) {
+ const char *msg;
Py_DECREF(retval);
- PyErr_SetString(PyExc_RuntimeError,
- "generator ignored GeneratorExit");
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check(self)) {
+ msg = "coroutine ignored GeneratorExit";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact(self)) {
+#if PY_VERSION_HEX < 0x03060000
+ msg = "async generator ignored GeneratorExit - might require Python 3.6+ finalisation (PEP 525)";
+#else
+ msg = "async generator ignored GeneratorExit";
+#endif
+ #endif
+ } else {
+ msg = "generator ignored GeneratorExit";
+ }
+ PyErr_SetString(PyExc_RuntimeError, msg);
return NULL;
}
raised_exception = PyErr_Occurred();
- if (!raised_exception
- || raised_exception == PyExc_StopIteration
- || raised_exception == PyExc_GeneratorExit
- || PyErr_GivenExceptionMatches(raised_exception, PyExc_GeneratorExit)
- || PyErr_GivenExceptionMatches(raised_exception, PyExc_StopIteration))
- {
+ if (likely(!raised_exception || __Pyx_PyErr_GivenExceptionMatches2(raised_exception, PyExc_GeneratorExit, PyExc_StopIteration))) {
// ignore these errors
if (raised_exception) PyErr_Clear();
Py_INCREF(Py_None);
@@ -766,55 +1083,61 @@
return NULL;
}
-static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) {
+static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject *val, PyObject *tb,
+ PyObject *args, int close_on_genexit) {
__pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
- PyObject *typ;
- PyObject *tb = NULL;
- PyObject *val = NULL;
PyObject *yf = gen->yieldfrom;
- if (!PyArg_UnpackTuple(args, (char *)"throw", 1, 3, &typ, &val, &tb))
- return NULL;
-
- if (unlikely(__Pyx_Coroutine_CheckRunning(gen)))
- return NULL;
+ if (unlikely(gen->is_running))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
if (yf) {
PyObject *ret;
Py_INCREF(yf);
- if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) {
+ if (__Pyx_PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && close_on_genexit) {
+ // Asynchronous generators *should not* be closed right away.
+ // We have to allow some awaits to work it through, hence the
+ // `close_on_genexit` parameter here.
int err = __Pyx_Coroutine_CloseIter(gen, yf);
Py_DECREF(yf);
__Pyx_Coroutine_Undelegate(gen);
if (err < 0)
- return __Pyx_Coroutine_MethodReturn(__Pyx_Coroutine_SendEx(gen, NULL));
+ return __Pyx_Coroutine_MethodReturn(self, __Pyx_Coroutine_SendEx(gen, NULL, 0));
goto throw_here;
}
gen->is_running = 1;
+ if (0
#ifdef __Pyx_Generator_USED
- if (__Pyx_Generator_CheckExact(yf)) {
- ret = __Pyx_Coroutine_Throw(yf, args);
- } else
+ || __Pyx_Generator_CheckExact(yf)
#endif
#ifdef __Pyx_Coroutine_USED
- if (__Pyx_Coroutine_CheckExact(yf)) {
- ret = __Pyx_Coroutine_Throw(yf, args);
- } else
+ || __Pyx_Coroutine_Check(yf)
#endif
- {
- PyObject *meth = __Pyx_PyObject_GetAttrStr(yf, PYIDENT("throw"));
+ ) {
+ ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit);
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ ret = __Pyx__Coroutine_Throw(((__pyx_CoroutineAwaitObject*)yf)->coroutine, typ, val, tb, args, close_on_genexit);
+ #endif
+ } else {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(yf, PYIDENT("throw"));
if (unlikely(!meth)) {
Py_DECREF(yf);
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ if (unlikely(PyErr_Occurred())) {
gen->is_running = 0;
return NULL;
}
- PyErr_Clear();
__Pyx_Coroutine_Undelegate(gen);
gen->is_running = 0;
goto throw_here;
}
- ret = PyObject_CallObject(meth, args);
+ if (likely(args)) {
+ ret = __Pyx_PyObject_Call(meth, args, NULL);
+ } else {
+ // "tb" or even "val" might be NULL, but that also correctly terminates the argument list
+ PyObject *cargs[4] = {NULL, typ, val, tb};
+ ret = __Pyx_PyObject_FastCall(meth, cargs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+ }
Py_DECREF(meth);
}
gen->is_running = 0;
@@ -822,23 +1145,40 @@
if (!ret) {
ret = __Pyx_Coroutine_FinishDelegation(gen);
}
- return __Pyx_Coroutine_MethodReturn(ret);
+ return __Pyx_Coroutine_MethodReturn(self, ret);
}
throw_here:
__Pyx_Raise(typ, val, tb, NULL);
- return __Pyx_Coroutine_MethodReturn(__Pyx_Coroutine_SendEx(gen, NULL));
+ return __Pyx_Coroutine_MethodReturn(self, __Pyx_Coroutine_SendEx(gen, NULL, 0));
}
-static int __Pyx_Coroutine_traverse(PyObject *self, visitproc visit, void *arg) {
- __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) {
+ PyObject *typ;
+ PyObject *val = NULL;
+ PyObject *tb = NULL;
+
+ if (unlikely(!PyArg_UnpackTuple(args, (char *)"throw", 1, 3, &typ, &val, &tb)))
+ return NULL;
+ return __Pyx__Coroutine_Throw(self, typ, val, tb, args, 1);
+}
+
+static CYTHON_INLINE int __Pyx_Coroutine_traverse_excstate(__Pyx_ExcInfoStruct *exc_state, visitproc visit, void *arg) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_VISIT(exc_state->exc_value);
+#else
+ Py_VISIT(exc_state->exc_type);
+ Py_VISIT(exc_state->exc_value);
+ Py_VISIT(exc_state->exc_traceback);
+#endif
+ return 0;
+}
+
+static int __Pyx_Coroutine_traverse(__pyx_CoroutineObject *gen, visitproc visit, void *arg) {
Py_VISIT(gen->closure);
Py_VISIT(gen->classobj);
Py_VISIT(gen->yieldfrom);
- Py_VISIT(gen->exc_type);
- Py_VISIT(gen->exc_value);
- Py_VISIT(gen->exc_traceback);
- return 0;
+ return __Pyx_Coroutine_traverse_excstate(&gen->gi_exc_state, visit, arg);
}
static int __Pyx_Coroutine_clear(PyObject *self) {
@@ -847,11 +1187,17 @@
Py_CLEAR(gen->closure);
Py_CLEAR(gen->classobj);
Py_CLEAR(gen->yieldfrom);
- Py_CLEAR(gen->exc_type);
- Py_CLEAR(gen->exc_value);
- Py_CLEAR(gen->exc_traceback);
+ __Pyx_Coroutine_ExceptionClear(&gen->gi_exc_state);
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)gen)->ag_finalizer);
+ }
+#endif
+ Py_CLEAR(gen->gi_code);
+ Py_CLEAR(gen->gi_frame);
Py_CLEAR(gen->gi_name);
Py_CLEAR(gen->gi_qualname);
+ Py_CLEAR(gen->gi_modulename);
return 0;
}
@@ -862,14 +1208,14 @@
if (gen->gi_weakreflist != NULL)
PyObject_ClearWeakRefs(self);
- if (gen->resume_label > 0) {
- // Generator is paused, so we need to close
+ if (gen->resume_label >= 0) {
+ // Generator is paused or unstarted, so we need to close
PyObject_GC_Track(self);
-#if PY_VERSION_HEX >= 0x030400a1
- if (PyObject_CallFinalizerFromDealloc(self))
+#if PY_VERSION_HEX >= 0x030400a1 && CYTHON_USE_TP_FINALIZE
+ if (unlikely(PyObject_CallFinalizerFromDealloc(self)))
#else
Py_TYPE(gen)->tp_del(self);
- if (self->ob_refcnt > 0)
+ if (unlikely(Py_REFCNT(self) > 0))
#endif
{
// resurrected. :(
@@ -878,44 +1224,114 @@
PyObject_GC_UnTrack(self);
}
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ /* We have to handle this case for asynchronous generators
+ right here, because this code has to be between UNTRACK
+ and GC_Del. */
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)self)->ag_finalizer);
+ }
+#endif
__Pyx_Coroutine_clear(self);
- PyObject_GC_Del(gen);
+ __Pyx_PyHeapTypeObject_GC_Del(gen);
}
static void __Pyx_Coroutine_del(PyObject *self) {
- PyObject *res;
PyObject *error_type, *error_value, *error_traceback;
__pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
__Pyx_PyThreadState_declare
- if (gen->resume_label <= 0)
- return ;
+ if (gen->resume_label < 0) {
+ // already terminated => nothing to clean up
+ return;
+ }
+
+#if !CYTHON_USE_TP_FINALIZE
+ // Temporarily resurrect the object.
+ assert(self->ob_refcnt == 0);
+ __Pyx_SET_REFCNT(self, 1);
+#endif
+
+ __Pyx_PyThreadState_assign
+
+ // Save the current exception, if any.
+ __Pyx_ErrFetch(&error_type, &error_value, &error_traceback);
+
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ __pyx_PyAsyncGenObject *agen = (__pyx_PyAsyncGenObject*)self;
+ PyObject *finalizer = agen->ag_finalizer;
+ if (finalizer && !agen->ag_closed) {
+ PyObject *res = __Pyx_PyObject_CallOneArg(finalizer, self);
+ if (unlikely(!res)) {
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_DECREF(res);
+ }
+ // Restore the saved exception.
+ __Pyx_ErrRestore(error_type, error_value, error_traceback);
+ return;
+ }
+ }
+#endif
+
+ if (unlikely(gen->resume_label == 0 && !error_value)) {
+#ifdef __Pyx_Coroutine_USED
+#ifdef __Pyx_Generator_USED
+ // only warn about (async) coroutines
+ if (!__Pyx_Generator_CheckExact(self))
+#endif
+ {
+ // untrack dead object as we are executing Python code (which might trigger GC)
+ PyObject_GC_UnTrack(self);
+#if PY_MAJOR_VERSION >= 3 /* PY_VERSION_HEX >= 0x03030000*/ || defined(PyErr_WarnFormat)
+ if (unlikely(PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname) < 0))
+ PyErr_WriteUnraisable(self);
+#else
+ {PyObject *msg;
+ char *cmsg;
+ #if CYTHON_COMPILING_IN_PYPY
+ msg = NULL;
+ cmsg = (char*) "coroutine was never awaited";
+ #else
+ char *cname;
+ PyObject *qualname;
+ qualname = gen->gi_qualname;
+ cname = PyString_AS_STRING(qualname);
+ msg = PyString_FromFormat("coroutine '%.50s' was never awaited", cname);
-#if PY_VERSION_HEX < 0x030400a1
- // Temporarily resurrect the object.
- assert(self->ob_refcnt == 0);
- self->ob_refcnt = 1;
+ if (unlikely(!msg)) {
+ PyErr_Clear();
+ cmsg = (char*) "coroutine was never awaited";
+ } else {
+ cmsg = PyString_AS_STRING(msg);
+ }
+ #endif
+ if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, cmsg, 1) < 0))
+ PyErr_WriteUnraisable(self);
+ Py_XDECREF(msg);}
#endif
-
- // Save the current exception, if any.
- __Pyx_PyThreadState_assign
- __Pyx_ErrFetch(&error_type, &error_value, &error_traceback);
-
- res = __Pyx_Coroutine_Close(self);
-
- if (res == NULL)
- PyErr_WriteUnraisable(self);
- else
- Py_DECREF(res);
+ PyObject_GC_Track(self);
+ }
+#endif /*__Pyx_Coroutine_USED*/
+ } else {
+ PyObject *res = __Pyx_Coroutine_Close(self);
+ if (unlikely(!res)) {
+ if (PyErr_Occurred())
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_DECREF(res);
+ }
+ }
// Restore the saved exception.
__Pyx_ErrRestore(error_type, error_value, error_traceback);
-#if PY_VERSION_HEX < 0x030400a1
+#if !CYTHON_USE_TP_FINALIZE
// Undo the temporary resurrection; can't use DECREF here, it would
// cause a recursive call.
- assert(self->ob_refcnt > 0);
- if (--self->ob_refcnt == 0) {
+ assert(Py_REFCNT(self) > 0);
+ if (likely(--self->ob_refcnt == 0)) {
// this is the normal path out
return;
}
@@ -923,12 +1339,12 @@
// close() resurrected it! Make it look like the original Py_DECREF
// never happened.
{
- Py_ssize_t refcnt = self->ob_refcnt;
+ Py_ssize_t refcnt = Py_REFCNT(self);
_Py_NewReference(self);
- self->ob_refcnt = refcnt;
+ __Pyx_SET_REFCNT(self, refcnt);
}
#if CYTHON_COMPILING_IN_CPYTHON
- assert(PyType_IS_GC(self->ob_type) &&
+ assert(PyType_IS_GC(Py_TYPE(self)) &&
_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
// If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
@@ -948,69 +1364,102 @@
}
static PyObject *
-__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self)
+__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self, void *context)
{
- Py_INCREF(self->gi_name);
- return self->gi_name;
+ PyObject *name = self->gi_name;
+ CYTHON_UNUSED_VAR(context);
+ // avoid NULL pointer dereference during garbage collection
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
}
static int
-__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value)
+__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value, void *context)
{
- PyObject *tmp;
-
+ CYTHON_UNUSED_VAR(context);
#if PY_MAJOR_VERSION >= 3
- if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ if (unlikely(value == NULL || !PyUnicode_Check(value)))
#else
- if (unlikely(value == NULL || !PyString_Check(value))) {
+ if (unlikely(value == NULL || !PyString_Check(value)))
#endif
+ {
PyErr_SetString(PyExc_TypeError,
"__name__ must be set to a string object");
return -1;
}
- tmp = self->gi_name;
Py_INCREF(value);
- self->gi_name = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(self->gi_name, value);
return 0;
}
static PyObject *
-__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self)
+__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self, void *context)
{
- Py_INCREF(self->gi_qualname);
- return self->gi_qualname;
+ PyObject *name = self->gi_qualname;
+ CYTHON_UNUSED_VAR(context);
+ // avoid NULL pointer dereference during garbage collection
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
}
static int
-__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value)
+__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value, void *context)
{
- PyObject *tmp;
-
+ CYTHON_UNUSED_VAR(context);
#if PY_MAJOR_VERSION >= 3
- if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ if (unlikely(value == NULL || !PyUnicode_Check(value)))
#else
- if (unlikely(value == NULL || !PyString_Check(value))) {
+ if (unlikely(value == NULL || !PyString_Check(value)))
#endif
+ {
PyErr_SetString(PyExc_TypeError,
"__qualname__ must be set to a string object");
return -1;
}
- tmp = self->gi_qualname;
Py_INCREF(value);
- self->gi_qualname = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(self->gi_qualname, value);
return 0;
}
+static PyObject *
+__Pyx_Coroutine_get_frame(__pyx_CoroutineObject *self, void *context)
+{
+ PyObject *frame = self->gi_frame;
+ CYTHON_UNUSED_VAR(context);
+ if (!frame) {
+ if (unlikely(!self->gi_code)) {
+ // Avoid doing something stupid, e.g. during garbage collection.
+ Py_RETURN_NONE;
+ }
+ frame = (PyObject *) PyFrame_New(
+ PyThreadState_Get(), /*PyThreadState *tstate,*/
+ (PyCodeObject*) self->gi_code, /*PyCodeObject *code,*/
+ $moddict_cname, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ if (unlikely(!frame))
+ return NULL;
+ // keep the frame cached once it's created
+ self->gi_frame = frame;
+ }
+ Py_INCREF(frame);
+ return frame;
+}
+
static __pyx_CoroutineObject *__Pyx__Coroutine_New(
- PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *closure,
+ PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
PyObject *name, PyObject *qualname, PyObject *module_name) {
__pyx_CoroutineObject *gen = PyObject_GC_New(__pyx_CoroutineObject, type);
-
- if (gen == NULL)
+ if (unlikely(!gen))
return NULL;
+ return __Pyx__Coroutine_NewInit(gen, body, code, closure, name, qualname, module_name);
+}
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
gen->body = body;
gen->closure = closure;
Py_XINCREF(closure);
@@ -1018,9 +1467,16 @@
gen->resume_label = 0;
gen->classobj = NULL;
gen->yieldfrom = NULL;
- gen->exc_type = NULL;
- gen->exc_value = NULL;
- gen->exc_traceback = NULL;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ gen->gi_exc_state.exc_value = NULL;
+ #else
+ gen->gi_exc_state.exc_type = NULL;
+ gen->gi_exc_state.exc_value = NULL;
+ gen->gi_exc_state.exc_traceback = NULL;
+ #endif
+#if CYTHON_USE_EXC_INFO_STACK
+ gen->gi_exc_state.previous_item = NULL;
+#endif
gen->gi_weakreflist = NULL;
Py_XINCREF(qualname);
gen->gi_qualname = qualname;
@@ -1028,6 +1484,9 @@
gen->gi_name = name;
Py_XINCREF(module_name);
gen->gi_modulename = module_name;
+ Py_XINCREF(code);
+ gen->gi_code = code;
+ gen->gi_frame = NULL;
PyObject_GC_Track(gen);
return gen;
@@ -1037,16 +1496,12 @@
//////////////////// Coroutine ////////////////////
//@requires: CoroutineBase
//@requires: PatchGeneratorABC
-
-typedef struct {
- PyObject_HEAD
- PyObject *coroutine;
-} __pyx_CoroutineAwaitObject;
+//@requires: ObjectHandling.c::PyObject_GenericGetAttrNoDict
static void __Pyx_CoroutineAwait_dealloc(PyObject *self) {
PyObject_GC_UnTrack(self);
Py_CLEAR(((__pyx_CoroutineAwaitObject*)self)->coroutine);
- PyObject_GC_Del(self);
+ __Pyx_PyHeapTypeObject_GC_Del(self);
}
static int __Pyx_CoroutineAwait_traverse(__pyx_CoroutineAwaitObject *self, visitproc visit, void *arg) {
@@ -1071,7 +1526,8 @@
return __Pyx_Coroutine_Throw(self->coroutine, args);
}
-static PyObject *__Pyx_CoroutineAwait_Close(__pyx_CoroutineAwaitObject *self) {
+static PyObject *__Pyx_CoroutineAwait_Close(__pyx_CoroutineAwaitObject *self, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
return __Pyx_Coroutine_Close(self->coroutine);
}
@@ -1081,12 +1537,31 @@
}
#if !CYTHON_COMPILING_IN_PYPY
-static PyObject *__Pyx_CoroutineAwait_no_new(CYTHON_UNUSED PyTypeObject *type, CYTHON_UNUSED PyObject *args, CYTHON_UNUSED PyObject *kwargs) {
+static PyObject *__Pyx_CoroutineAwait_no_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
+ CYTHON_UNUSED_VAR(type);
+ CYTHON_UNUSED_VAR(args);
+ CYTHON_UNUSED_VAR(kwargs);
PyErr_SetString(PyExc_TypeError, "cannot instantiate type, use 'await coroutine' instead");
return NULL;
}
#endif
+// In earlier versions of Python an object with no __dict__ and not __slots__ is assumed
+// to be pickleable by default. Coroutine-wrappers have significant state so shouldn't be.
+// Therefore provide a default implementation.
+// Something similar applies to heaptypes (i.e. with type_specs) with protocols 0 and 1
+// even in more recent versions.
+// We are applying this to all Python versions (hence the commented out version guard)
+// to make the behaviour explicit.
+// #if PY_VERSION_HEX < 0x03060000 || CYTHON_USE_TYPE_SPECS
+static PyObject *__Pyx_CoroutineAwait_reduce_ex(__pyx_CoroutineAwaitObject *self, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ PyErr_Format(PyExc_TypeError, "cannot pickle '%.200s' object",
+ Py_TYPE(self)->tp_name);
+ return NULL;
+}
+// #endif
+
static PyMethodDef __pyx_CoroutineAwait_methods[] = {
{"send", (PyCFunction) __Pyx_CoroutineAwait_Send, METH_O,
(char*) PyDoc_STR("send(arg) -> send 'arg' into coroutine,\nreturn next yielded value or raise StopIteration.")},
@@ -1094,12 +1569,40 @@
(char*) PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in coroutine,\nreturn next yielded value or raise StopIteration.")},
{"close", (PyCFunction) __Pyx_CoroutineAwait_Close, METH_NOARGS,
(char*) PyDoc_STR("close() -> raise GeneratorExit inside coroutine.")},
+// only needed with type-specs or version<3.6, but included in all versions for clarity
+// #if PY_VERSION_HEX < 0x03060000 || CYTHON_USE_TYPE_SPECS
+ {"__reduce_ex__", (PyCFunction) __Pyx_CoroutineAwait_reduce_ex, METH_O, 0},
+ {"__reduce__", (PyCFunction) __Pyx_CoroutineAwait_reduce_ex, METH_NOARGS, 0},
+// #endif
{0, 0, 0, 0}
};
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_CoroutineAwaitType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_CoroutineAwait_dealloc},
+ {Py_tp_traverse, (void *)__Pyx_CoroutineAwait_traverse},
+ {Py_tp_clear, (void *)__Pyx_CoroutineAwait_clear},
+#if !CYTHON_COMPILING_IN_PYPY
+ {Py_tp_new, (void *)__Pyx_CoroutineAwait_no_new},
+#endif
+ {Py_tp_methods, (void *)__pyx_CoroutineAwait_methods},
+ {Py_tp_iter, (void *)__Pyx_CoroutineAwait_self},
+ {Py_tp_iternext, (void *)__Pyx_CoroutineAwait_Next},
+ {0, 0},
+};
+
+static PyType_Spec __pyx_CoroutineAwaitType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "coroutine_wrapper",
+ sizeof(__pyx_CoroutineAwaitObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ __pyx_CoroutineAwaitType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
static PyTypeObject __pyx_CoroutineAwaitType_type = {
PyVarObject_HEAD_INIT(0, 0)
- "coroutine_wrapper", /*tp_name*/
+ __PYX_TYPE_MODULE_PREFIX "coroutine_wrapper", /*tp_name*/
sizeof(__pyx_CoroutineAwaitObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __Pyx_CoroutineAwait_dealloc,/*tp_dealloc*/
@@ -1152,90 +1655,45 @@
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
};
+#endif /* CYTHON_USE_TYPE_SPECS */
+#if PY_VERSION_HEX < 0x030500B1 || defined(__Pyx_IterableCoroutine_USED) || CYTHON_USE_ASYNC_SLOTS
static CYTHON_INLINE PyObject *__Pyx__Coroutine_await(PyObject *coroutine) {
-#if CYTHON_COMPILING_IN_CPYTHON
__pyx_CoroutineAwaitObject *await = PyObject_GC_New(__pyx_CoroutineAwaitObject, __pyx_CoroutineAwaitType);
-#else
- __pyx_CoroutineAwaitObject *await = (__pyx_CoroutineAwaitObject*)
- __pyx_CoroutineAwaitType->tp_new(__pyx_CoroutineAwaitType, __pyx_empty_tuple, NULL);
-#endif
if (unlikely(!await)) return NULL;
Py_INCREF(coroutine);
await->coroutine = coroutine;
-#if CYTHON_COMPILING_IN_CPYTHON
PyObject_GC_Track(await);
-#endif
return (PyObject*)await;
}
+#endif
+
+#if PY_VERSION_HEX < 0x030500B1
+static PyObject *__Pyx_Coroutine_await_method(PyObject *coroutine, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ return __Pyx__Coroutine_await(coroutine);
+}
+#endif
+#if defined(__Pyx_IterableCoroutine_USED) || CYTHON_USE_ASYNC_SLOTS
static PyObject *__Pyx_Coroutine_await(PyObject *coroutine) {
- if (unlikely(!coroutine || !__Pyx_Coroutine_CheckExact(coroutine))) {
+ if (unlikely(!coroutine || !__Pyx_Coroutine_Check(coroutine))) {
PyErr_SetString(PyExc_TypeError, "invalid input, expected coroutine");
return NULL;
}
return __Pyx__Coroutine_await(coroutine);
}
-
-static void __Pyx_Coroutine_check_and_dealloc(PyObject *self) {
- __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
-
- if (gen->resume_label == 0 && !PyErr_Occurred()) {
- // untrack dead object as we are executing Python code (which might trigger GC)
- PyObject_GC_UnTrack(self);
-#if PY_VERSION_HEX >= 0x03030000 || defined(PyErr_WarnFormat)
- PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname);
- PyErr_Clear(); /* just in case, must not keep a live exception during GC */
-#else
- {PyObject *msg;
- char *cmsg;
- #if CYTHON_COMPILING_IN_PYPY
- msg = NULL;
- cmsg = (char*) "coroutine was never awaited";
- #else
- char *cname;
- PyObject *qualname;
- #if PY_MAJOR_VERSION >= 3
- qualname = PyUnicode_AsUTF8String(gen->gi_qualname);
- if (likely(qualname)) {
- cname = PyBytes_AS_STRING(qualname);
- } else {
- PyErr_Clear();
- cname = (char*) "?";
- }
- msg = PyBytes_FromFormat(
- #else
- qualname = gen->gi_qualname;
- cname = PyString_AS_STRING(qualname);
- msg = PyString_FromFormat(
- #endif
- "coroutine '%.50s' was never awaited", cname);
-
- #if PY_MAJOR_VERSION >= 3
- Py_XDECREF(qualname);
- #endif
-
- if (unlikely(!msg)) {
- PyErr_Clear();
- cmsg = (char*) "coroutine was never awaited";
- } else {
- #if PY_MAJOR_VERSION >= 3
- cmsg = PyBytes_AS_STRING(msg);
- #else
- cmsg = PyString_AS_STRING(msg);
- #endif
- }
- #endif
- if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, cmsg, 1) < 0))
- PyErr_WriteUnraisable(self);
- Py_XDECREF(msg);}
#endif
- PyObject_GC_Track(self);
- }
-
- __Pyx_Coroutine_dealloc(self);
-}
#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
static PyObject *__Pyx_Coroutine_compare(PyObject *obj, PyObject *other, int op) {
@@ -1256,10 +1714,10 @@
(char*) PyDoc_STR("send(arg) -> send 'arg' into coroutine,\nreturn next iterated value or raise StopIteration.")},
{"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS,
(char*) PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in coroutine,\nreturn next iterated value or raise StopIteration.")},
- {"close", (PyCFunction) __Pyx_Coroutine_Close, METH_NOARGS,
+ {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS,
(char*) PyDoc_STR("close() -> raise GeneratorExit inside coroutine.")},
#if PY_VERSION_HEX < 0x030500B1
- {"__await__", (PyCFunction) __Pyx_Coroutine_await, METH_NOARGS,
+ {"__await__", (PyCFunction) __Pyx_Coroutine_await_method, METH_NOARGS,
(char*) PyDoc_STR("__await__() -> return an iterator to be used in await expression.")},
#endif
{0, 0, 0, 0}
@@ -1269,7 +1727,11 @@
{(char *) "cr_running", T_BOOL, offsetof(__pyx_CoroutineObject, is_running), READONLY, NULL},
{(char*) "cr_await", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY,
(char*) PyDoc_STR("object being awaited, or None")},
- {(char *) "__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), PY_WRITE_RESTRICTED, 0},
+ {(char*) "cr_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL},
+ {(char *) "__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0},
+#if CYTHON_USE_TYPE_SPECS
+ {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0},
+#endif
{0, 0, 0, 0, 0}
};
@@ -1278,23 +1740,52 @@
(char*) PyDoc_STR("name of the coroutine"), 0},
{(char *) "__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname,
(char*) PyDoc_STR("qualified name of the coroutine"), 0},
+ {(char *) "cr_frame", (getter)__Pyx_Coroutine_get_frame, NULL,
+ (char*) PyDoc_STR("Frame of the coroutine"), 0},
{0, 0, 0, 0, 0}
};
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_CoroutineType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_am_await, (void *)&__Pyx_Coroutine_await},
+ {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse},
+ {Py_tp_methods, (void *)__pyx_Coroutine_methods},
+ {Py_tp_members, (void *)__pyx_Coroutine_memberlist},
+ {Py_tp_getset, (void *)__pyx_Coroutine_getsets},
+ {Py_tp_getattro, (void *) __Pyx_PyObject_GenericGetAttrNoDict},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+ {0, 0},
+};
+
+static PyType_Spec __pyx_CoroutineType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "coroutine",
+ sizeof(__pyx_CoroutineObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ __pyx_CoroutineType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
#if CYTHON_USE_ASYNC_SLOTS
static __Pyx_PyAsyncMethodsStruct __pyx_Coroutine_as_async = {
__Pyx_Coroutine_await, /*am_await*/
0, /*am_aiter*/
0, /*am_anext*/
+#if PY_VERSION_HEX >= 0x030A00A3
+ 0, /*am_send*/
+#endif
};
#endif
static PyTypeObject __pyx_CoroutineType_type = {
PyVarObject_HEAD_INIT(0, 0)
- "coroutine", /*tp_name*/
+ __PYX_TYPE_MODULE_PREFIX "coroutine", /*tp_name*/
sizeof(__pyx_CoroutineObject), /*tp_basicsize*/
0, /*tp_itemsize*/
- (destructor) __Pyx_Coroutine_check_and_dealloc,/*tp_dealloc*/
+ (destructor) __Pyx_Coroutine_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
@@ -1317,14 +1808,14 @@
0, /*tp_doc*/
(traverseproc) __Pyx_Coroutine_traverse, /*tp_traverse*/
0, /*tp_clear*/
-#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
+#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
// in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
__Pyx_Coroutine_compare, /*tp_richcompare*/
#else
0, /*tp_richcompare*/
#endif
offsetof(__pyx_CoroutineObject, gi_weakreflist), /*tp_weaklistoffset*/
-// no tp_iter() as iterator is only available through __await__()
+ // no tp_iter() as iterator is only available through __await__()
0, /*tp_iter*/
0, /*tp_iternext*/
__pyx_Coroutine_methods, /*tp_methods*/
@@ -1345,41 +1836,205 @@
0, /*tp_cache*/
0, /*tp_subclasses*/
0, /*tp_weaklist*/
-#if PY_VERSION_HEX >= 0x030400a1
+#if CYTHON_USE_TP_FINALIZE
0, /*tp_del*/
#else
__Pyx_Coroutine_del, /*tp_del*/
#endif
0, /*tp_version_tag*/
-#if PY_VERSION_HEX >= 0x030400a1
+#if CYTHON_USE_TP_FINALIZE
__Pyx_Coroutine_del, /*tp_finalize*/
+#elif PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_finalize*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
#endif
};
+#endif /* CYTHON_USE_TYPE_SPECS */
-static int __pyx_Coroutine_init(void) {
+static int __pyx_Coroutine_init(PyObject *module) {
// on Windows, C-API functions can't be used in slots statically
- __pyx_CoroutineType_type.tp_getattro = PyObject_GenericGetAttr;
-
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_CoroutineType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CoroutineType_spec, NULL);
+#else
+ (void) module;
+ __pyx_CoroutineType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
__pyx_CoroutineType = __Pyx_FetchCommonType(&__pyx_CoroutineType_type);
+#endif
if (unlikely(!__pyx_CoroutineType))
return -1;
+#ifdef __Pyx_IterableCoroutine_USED
+ if (unlikely(__pyx_IterableCoroutine_init(module) == -1))
+ return -1;
+#endif
+
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_CoroutineAwaitType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CoroutineAwaitType_spec, NULL);
+#else
__pyx_CoroutineAwaitType = __Pyx_FetchCommonType(&__pyx_CoroutineAwaitType_type);
+#endif
if (unlikely(!__pyx_CoroutineAwaitType))
return -1;
return 0;
}
+
+//////////////////// IterableCoroutine.proto ////////////////////
+
+#define __Pyx_IterableCoroutine_USED
+
+static PyTypeObject *__pyx_IterableCoroutineType = 0;
+
+#undef __Pyx_Coroutine_Check
+#define __Pyx_Coroutine_Check(obj) (__Pyx_Coroutine_CheckExact(obj) || __Pyx_IS_TYPE(obj, __pyx_IterableCoroutineType))
+
+#define __Pyx_IterableCoroutine_New(body, code, closure, name, qualname, module_name) \
+ __Pyx__Coroutine_New(__pyx_IterableCoroutineType, body, code, closure, name, qualname, module_name)
+
+static int __pyx_IterableCoroutine_init(PyObject *module);/*proto*/
+
+
+//////////////////// IterableCoroutine ////////////////////
+//@requires: Coroutine
+//@requires: CommonStructures.c::FetchCommonType
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_IterableCoroutineType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_am_await, (void *)&__Pyx_Coroutine_await},
+ {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse},
+ {Py_tp_iter, (void *)__Pyx_Coroutine_await},
+ {Py_tp_iternext, (void *)__Pyx_Generator_Next},
+ {Py_tp_methods, (void *)__pyx_Coroutine_methods},
+ {Py_tp_members, (void *)__pyx_Coroutine_memberlist},
+ {Py_tp_getset, (void *)__pyx_Coroutine_getsets},
+ {Py_tp_getattro, (void *) __Pyx_PyObject_GenericGetAttrNoDict},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+ {0, 0},
+};
+
+static PyType_Spec __pyx_IterableCoroutineType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "iterable_coroutine",
+ sizeof(__pyx_CoroutineObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ __pyx_IterableCoroutineType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
+static PyTypeObject __pyx_IterableCoroutineType_type = {
+ PyVarObject_HEAD_INIT(0, 0)
+ __PYX_TYPE_MODULE_PREFIX "iterable_coroutine", /*tp_name*/
+ sizeof(__pyx_CoroutineObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor) __Pyx_Coroutine_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+#if CYTHON_USE_ASYNC_SLOTS
+ &__pyx_Coroutine_as_async, /*tp_as_async (tp_reserved) - Py3 only! */
+#else
+ 0, /*tp_reserved*/
+#endif
+ 0, /*tp_repr*/
+ 0, /*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_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ 0, /*tp_doc*/
+ (traverseproc) __Pyx_Coroutine_traverse, /*tp_traverse*/
+ 0, /*tp_clear*/
+#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
+ // in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
+ __Pyx_Coroutine_compare, /*tp_richcompare*/
+#else
+ 0, /*tp_richcompare*/
+#endif
+ offsetof(__pyx_CoroutineObject, gi_weakreflist), /*tp_weaklistoffset*/
+ // enable iteration for legacy support of asyncio yield-from protocol
+ __Pyx_Coroutine_await, /*tp_iter*/
+ (iternextfunc) __Pyx_Generator_Next, /*tp_iternext*/
+ __pyx_Coroutine_methods, /*tp_methods*/
+ __pyx_Coroutine_memberlist, /*tp_members*/
+ __pyx_Coroutine_getsets, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+#if PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_del*/
+#else
+ __Pyx_Coroutine_del, /*tp_del*/
+#endif
+ 0, /*tp_version_tag*/
+#if PY_VERSION_HEX >= 0x030400a1 && !CYTHON_COMPILING_IN_PYPY
+ __Pyx_Coroutine_del, /*tp_finalize*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
+};
+#endif /* CYTHON_USE_TYPE_SPECS */
+
+
+static int __pyx_IterableCoroutine_init(PyObject *module) {
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_IterableCoroutineType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_IterableCoroutineType_spec, NULL);
+#else
+ (void) module;
+ __pyx_IterableCoroutineType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
+ __pyx_IterableCoroutineType = __Pyx_FetchCommonType(&__pyx_IterableCoroutineType_type);
+#endif
+ if (unlikely(!__pyx_IterableCoroutineType))
+ return -1;
+ return 0;
+}
+
+
//////////////////// Generator ////////////////////
//@requires: CoroutineBase
//@requires: PatchGeneratorABC
+//@requires: ObjectHandling.c::PyObject_GenericGetAttrNoDict
static PyMethodDef __pyx_Generator_methods[] = {
{"send", (PyCFunction) __Pyx_Coroutine_Send, METH_O,
(char*) PyDoc_STR("send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")},
{"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS,
(char*) PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")},
- {"close", (PyCFunction) __Pyx_Coroutine_Close, METH_NOARGS,
+ {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS,
(char*) PyDoc_STR("close() -> raise GeneratorExit inside generator.")},
{0, 0, 0, 0}
};
@@ -1388,6 +2043,11 @@
{(char *) "gi_running", T_BOOL, offsetof(__pyx_CoroutineObject, is_running), READONLY, NULL},
{(char*) "gi_yieldfrom", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY,
(char*) PyDoc_STR("object being iterated by 'yield from', or None")},
+ {(char*) "gi_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL},
+ {(char *) "__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0},
+#if CYTHON_USE_TYPE_SPECS
+ {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0},
+#endif
{0, 0, 0, 0, 0}
};
@@ -1396,19 +2056,46 @@
(char*) PyDoc_STR("name of the generator"), 0},
{(char *) "__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname,
(char*) PyDoc_STR("qualified name of the generator"), 0},
+ {(char *) "gi_frame", (getter)__Pyx_Coroutine_get_frame, NULL,
+ (char*) PyDoc_STR("Frame of the generator"), 0},
{0, 0, 0, 0, 0}
};
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_GeneratorType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse},
+ {Py_tp_iter, (void *)PyObject_SelfIter},
+ {Py_tp_iternext, (void *)__Pyx_Generator_Next},
+ {Py_tp_methods, (void *)__pyx_Generator_methods},
+ {Py_tp_members, (void *)__pyx_Generator_memberlist},
+ {Py_tp_getset, (void *)__pyx_Generator_getsets},
+ {Py_tp_getattro, (void *) __Pyx_PyObject_GenericGetAttrNoDict},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+ {0, 0},
+};
+
+static PyType_Spec __pyx_GeneratorType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "generator",
+ sizeof(__pyx_CoroutineObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ __pyx_GeneratorType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
static PyTypeObject __pyx_GeneratorType_type = {
PyVarObject_HEAD_INIT(0, 0)
- "generator", /*tp_name*/
+ __PYX_TYPE_MODULE_PREFIX "generator", /*tp_name*/
sizeof(__pyx_CoroutineObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __Pyx_Coroutine_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
- 0, /*tp_compare / tp_as_async*/
+ 0, /*tp_as_async*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
@@ -1445,23 +2132,39 @@
0, /*tp_cache*/
0, /*tp_subclasses*/
0, /*tp_weaklist*/
-#if PY_VERSION_HEX >= 0x030400a1
+#if CYTHON_USE_TP_FINALIZE
0, /*tp_del*/
#else
__Pyx_Coroutine_del, /*tp_del*/
#endif
0, /*tp_version_tag*/
-#if PY_VERSION_HEX >= 0x030400a1
+#if CYTHON_USE_TP_FINALIZE
__Pyx_Coroutine_del, /*tp_finalize*/
+#elif PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_finalize*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
#endif
};
+#endif /* CYTHON_USE_TYPE_SPECS */
-static int __pyx_Generator_init(void) {
+static int __pyx_Generator_init(PyObject *module) {
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_GeneratorType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_GeneratorType_spec, NULL);
+#else
+ (void) module;
// on Windows, C-API functions can't be used in slots statically
- __pyx_GeneratorType_type.tp_getattro = PyObject_GenericGetAttr;
+ __pyx_GeneratorType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict;
__pyx_GeneratorType_type.tp_iter = PyObject_SelfIter;
-
__pyx_GeneratorType = __Pyx_FetchCommonType(&__pyx_GeneratorType_type);
+#endif
if (unlikely(!__pyx_GeneratorType)) {
return -1;
}
@@ -1483,13 +2186,15 @@
// 1) Instantiating an exception just to pass back a value is costly.
// 2) CPython 3.3 <= x < 3.5b1 crash in yield-from when the StopIteration is not instantiated.
// 3) Passing a tuple as value into PyErr_SetObject() passes its items on as arguments.
-// 4) If there is currently an exception being handled, we need to chain it.
+// 4) Passing an exception as value will interpret it as an exception on unpacking and raise it (or unpack its value).
+// 5) If there is currently an exception being handled, we need to chain it.
static void __Pyx__ReturnWithStopIteration(PyObject* value) {
PyObject *exc, *args;
-#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_PYSTON
+#if CYTHON_COMPILING_IN_CPYTHON
__Pyx_PyThreadState_declare
- if ((PY_VERSION_HEX >= 0x03030000 && PY_VERSION_HEX < 0x030500B1) || unlikely(PyTuple_Check(value))) {
+ if ((PY_VERSION_HEX >= 0x03030000 && PY_VERSION_HEX < 0x030500B1)
+ || unlikely(PyTuple_Check(value) || PyExceptionInstance_Check(value))) {
args = PyTuple_New(1);
if (unlikely(!args)) return;
Py_INCREF(value);
@@ -1502,13 +2207,20 @@
Py_INCREF(value);
exc = value;
}
+ #if CYTHON_FAST_THREAD_STATE
__Pyx_PyThreadState_assign
- if (!$local_tstate_cname->exc_type) {
+ #if CYTHON_USE_EXC_INFO_STACK
+ if (!$local_tstate_cname->exc_info->exc_value)
+ #else
+ if (!$local_tstate_cname->exc_type)
+ #endif
+ {
// no chaining needed => avoid the overhead in PyErr_SetObject()
Py_INCREF(PyExc_StopIteration);
__Pyx_ErrRestore(PyExc_StopIteration, exc, NULL);
return;
}
+ #endif
#else
args = PyTuple_Pack(1, value);
if (unlikely(!args)) return;
@@ -1579,6 +2291,10 @@
//////////////////// PatchGeneratorABC ////////////////////
//@requires: PatchModuleWithCoroutine
+#ifndef CYTHON_REGISTER_ABCS
+#define CYTHON_REGISTER_ABCS 1
+#endif
+
#if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED)
static PyObject* __Pyx_patch_abc_module(PyObject *module); /*proto*/
static PyObject* __Pyx_patch_abc_module(PyObject *module) {
@@ -1601,13 +2317,13 @@
static int __Pyx_patch_abc(void) {
#if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED)
static int abc_patched = 0;
- if (!abc_patched) {
+ if (CYTHON_REGISTER_ABCS && !abc_patched) {
PyObject *module;
- module = PyImport_ImportModule((PY_VERSION_HEX >= 0x03030000) ? "collections.abc" : "collections");
- if (!module) {
+ module = PyImport_ImportModule((PY_MAJOR_VERSION >= 3) ? "collections.abc" : "collections");
+ if (unlikely(!module)) {
PyErr_WriteUnraisable(NULL);
if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning,
- ((PY_VERSION_HEX >= 0x03030000) ?
+ ((PY_MAJOR_VERSION >= 3) ?
"Cython module failed to register with collections.abc module" :
"Cython module failed to register with collections module"), 1) < 0)) {
return -1;
@@ -1631,7 +2347,7 @@
}
#else
// avoid "unused" warning for __Pyx_Coroutine_patch_module()
- if (0) __Pyx_Coroutine_patch_module(NULL, NULL);
+ if ((0)) __Pyx_Coroutine_patch_module(NULL, NULL);
#endif
return 0;
}
@@ -1672,7 +2388,8 @@
);
} else {
PyErr_Clear();
-#if PY_VERSION_HEX < 0x03040200
+// Always enable fallback: even if we compile against 3.4.2, we might be running on 3.4.1 at some point.
+//#if PY_VERSION_HEX < 0x03040200
// Py3.4.1 used to have asyncio.tasks instead of asyncio.coroutines
package = __Pyx_Import(PYIDENT("asyncio.tasks"), NULL, 0);
if (unlikely(!package)) goto asyncio_done;
@@ -1693,15 +2410,15 @@
old_types.add(_cython_generator_type)
""")
);
-#endif
+//#endif
// Py < 0x03040200
}
Py_DECREF(package);
if (unlikely(!patch_module)) goto ignore;
-#if PY_VERSION_HEX < 0x03040200
+//#if PY_VERSION_HEX < 0x03040200
asyncio_done:
PyErr_Clear();
-#endif
+//#endif
asyncio_patched = 1;
#ifdef __Pyx_Generator_USED
// now patch inspect.isgenerator() by looking up the imported module in the patched asyncio module
@@ -1723,7 +2440,7 @@
}
#else
// avoid "unused" warning for __Pyx_patch_inspect()
- if (0) return __Pyx_patch_inspect(module);
+ if ((0)) return __Pyx_patch_inspect(module);
#endif
}
return module;
@@ -1735,7 +2452,7 @@
}
#else
// avoid "unused" warning for __Pyx_Coroutine_patch_module()
- if (0) return __Pyx_patch_inspect(__Pyx_Coroutine_patch_module(module, NULL));
+ if ((0)) return __Pyx_patch_inspect(__Pyx_Coroutine_patch_module(module, NULL));
#endif
return module;
}
@@ -1770,7 +2487,7 @@
}
#else
// avoid "unused" warning for __Pyx_Coroutine_patch_module()
- if (0) return __Pyx_Coroutine_patch_module(module, NULL);
+ if ((0)) return __Pyx_Coroutine_patch_module(module, NULL);
#endif
return module;
}
@@ -1780,11 +2497,15 @@
#define __Pyx_StopAsyncIteration_USED
static PyObject *__Pyx_PyExc_StopAsyncIteration;
-static int __pyx_StopAsyncIteration_init(void); /*proto*/
+static int __pyx_StopAsyncIteration_init(PyObject *module); /*proto*/
//////////////////// StopAsyncIteration ////////////////////
#if PY_VERSION_HEX < 0x030500B1
+#if CYTHON_USE_TYPE_SPECS
+#error Using async coroutines with type specs requires Python 3.5 or later.
+#else
+
static PyTypeObject __Pyx__PyExc_StopAsyncIteration_type = {
PyVarObject_HEAD_INIT(0, 0)
"StopAsyncIteration", /*tp_name*/
@@ -1836,11 +2557,16 @@
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
+#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM+0 >= 0x06000000
+ 0, /*tp_pypy_flags*/
+#endif
};
#endif
+#endif
-static int __pyx_StopAsyncIteration_init(void) {
+static int __pyx_StopAsyncIteration_init(PyObject *module) {
#if PY_VERSION_HEX >= 0x030500B1
+ (void) module;
__Pyx_PyExc_StopAsyncIteration = PyExc_StopAsyncIteration;
#else
PyObject *builtins = PyEval_GetBuiltins();
@@ -1858,10 +2584,11 @@
__Pyx__PyExc_StopAsyncIteration_type.tp_dictoffset = ((PyTypeObject*)PyExc_BaseException)->tp_dictoffset;
__Pyx__PyExc_StopAsyncIteration_type.tp_base = (PyTypeObject*)PyExc_Exception;
+ (void) module;
__Pyx_PyExc_StopAsyncIteration = (PyObject*) __Pyx_FetchCommonType(&__Pyx__PyExc_StopAsyncIteration_type);
if (unlikely(!__Pyx_PyExc_StopAsyncIteration))
return -1;
- if (builtins && unlikely(PyMapping_SetItemString(builtins, (char*) "StopAsyncIteration", __Pyx_PyExc_StopAsyncIteration) < 0))
+ if (likely(builtins) && unlikely(PyMapping_SetItemString(builtins, (char*) "StopAsyncIteration", __Pyx_PyExc_StopAsyncIteration) < 0))
return -1;
#endif
return 0;
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CpdefEnums.pyx cython-0.20.1+1~202203241016-9537/Cython/Utility/CpdefEnums.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CpdefEnums.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CpdefEnums.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -6,10 +6,7 @@
int PY_VERSION_HEX
cdef object __Pyx_OrderedDict
-if PY_VERSION_HEX >= 0x02070000:
- from collections import OrderedDict as __Pyx_OrderedDict
-else:
- __Pyx_OrderedDict = dict
+from collections import OrderedDict as __Pyx_OrderedDict
@cython.internal
cdef class __Pyx_EnumMeta(type):
@@ -23,8 +20,7 @@
# @cython.internal
cdef object __Pyx_EnumBase
-class __Pyx_EnumBase(int):
- __metaclass__ = __Pyx_EnumMeta
+class __Pyx_EnumBase(int, metaclass=__Pyx_EnumMeta):
def __new__(cls, value, name=None):
for v in cls:
if v == value:
@@ -55,12 +51,38 @@
('{{item}}', {{item}}),
{{endfor}}
]))
+ {{if enum_doc is not None}}
+ {{name}}.__doc__ = {{ repr(enum_doc) }}
+ {{endif}}
+
{{for item in items}}
__Pyx_globals['{{item}}'] = {{name}}.{{item}}
{{endfor}}
else:
class {{name}}(__Pyx_EnumBase):
- pass
+ {{ repr(enum_doc) if enum_doc is not None else 'pass' }}
{{for item in items}}
__Pyx_globals['{{item}}'] = {{name}}({{item}}, '{{item}}')
{{endfor}}
+
+#################### CppScopedEnumType ####################
+#@requires: EnumBase
+cdef dict __Pyx_globals = globals()
+
+if PY_VERSION_HEX >= 0x03040000:
+ # create new IntEnum()
+ __Pyx_globals["{{name}}"] = __Pyx_EnumBase('{{name}}', __Pyx_OrderedDict([
+ {{for item in items}}
+ ('{{item}}', <{{underlying_type}}>({{name}}.{{item}})),
+ {{endfor}}
+ ]))
+
+else:
+ __Pyx_globals["{{name}}"] = type('{{name}}', (__Pyx_EnumBase,), {})
+ {{for item in items}}
+ __Pyx_globals["{{name}}"](<{{underlying_type}}>({{name}}.{{item}}), '{{item}}')
+ {{endfor}}
+
+{{if enum_doc is not None}}
+__Pyx_globals["{{name}}"].__doc__ = {{ repr(enum_doc) }}
+{{endif}}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CppConvert.pyx cython-0.20.1+1~202203241016-9537/Cython/Utility/CppConvert.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CppConvert.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CppConvert.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -5,14 +5,14 @@
cdef extern from *:
cdef cppclass string "{{type}}":
- string()
- string(char* c_str, size_t size)
- cdef char* __Pyx_PyObject_AsStringAndSize(object, Py_ssize_t*) except NULL
+ string() except +
+ string(char* c_str, size_t size) except +
+ cdef const char* __Pyx_PyObject_AsStringAndSize(object, Py_ssize_t*) except NULL
@cname("{{cname}}")
cdef string {{cname}}(object o) except *:
- cdef Py_ssize_t length
- cdef char* data = __Pyx_PyObject_AsStringAndSize(o, &length)
+ cdef Py_ssize_t length = 0
+ cdef const char* data = __Pyx_PyObject_AsStringAndSize(o, &length)
return string(data, length)
@@ -27,7 +27,7 @@
{{for py_type in ['PyObject', 'PyUnicode', 'PyStr', 'PyBytes', 'PyByteArray']}}
cdef extern from *:
- cdef object __Pyx_{{py_type}}_FromStringAndSize(char*, size_t)
+ cdef object __Pyx_{{py_type}}_FromStringAndSize(const char*, size_t)
@cname("{{cname.replace("PyObject", py_type, 1)}}")
cdef inline object {{cname.replace("PyObject", py_type, 1)}}(const string& s):
@@ -37,47 +37,59 @@
#################### vector.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass vector "std::vector" [T]:
- void push_back(T&)
+ void push_back(T&) except +
@cname("{{cname}}")
cdef vector[X] {{cname}}(object o) except *:
cdef vector[X] v
for item in o:
- v.push_back(X_from_py(item))
+ v.push_back(item)
return v
#################### vector.to_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
- cdef cppclass vector "const std::vector" [T]:
+ cdef cppclass vector "std::vector" [T]:
size_t size()
T& operator[](size_t)
-@cname("{{cname}}")
-cdef object {{cname}}(vector[X]& v):
- return [X_to_py(v[i]) for i in range(v.size())]
+cdef extern from "Python.h":
+ void Py_INCREF(object)
+ list PyList_New(Py_ssize_t size)
+ void PyList_SET_ITEM(object list, Py_ssize_t i, object o)
+ cdef Py_ssize_t PY_SSIZE_T_MAX
+
+@cname("{{cname}}")
+cdef object {{cname}}(const vector[X]& v):
+ if v.size() > PY_SSIZE_T_MAX:
+ raise MemoryError()
+
+ o = PyList_New( v.size())
+
+ cdef Py_ssize_t i
+ cdef object item
+
+ for i in range(v.size()):
+ item = v[i]
+ Py_INCREF(item)
+ PyList_SET_ITEM(o, i, item)
+ return o
#################### list.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass cpp_list "std::list" [T]:
- void push_back(T&)
+ void push_back(T&) except +
@cname("{{cname}}")
cdef cpp_list[X] {{cname}}(object o) except *:
cdef cpp_list[X] l
for item in o:
- l.push_back(X_from_py(item))
+ l.push_back(item)
return l
@@ -85,8 +97,6 @@
cimport cython
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass cpp_list "std::list" [T]:
cppclass const_iterator:
@@ -95,30 +105,46 @@
bint operator!=(const_iterator)
const_iterator begin()
const_iterator end()
+ size_t size()
+
+cdef extern from "Python.h":
+ void Py_INCREF(object)
+ list PyList_New(Py_ssize_t size)
+ void PyList_SET_ITEM(object list, Py_ssize_t i, object o)
+ cdef Py_ssize_t PY_SSIZE_T_MAX
@cname("{{cname}}")
cdef object {{cname}}(const cpp_list[X]& v):
- o = []
+ if v.size() > PY_SSIZE_T_MAX:
+ raise MemoryError()
+
+ o = PyList_New( v.size())
+
+ cdef object item
+ cdef Py_ssize_t i = 0
cdef cpp_list[X].const_iterator iter = v.begin()
+
while iter != v.end():
- o.append(X_to_py(cython.operator.dereference(iter)))
+ item = cython.operator.dereference(iter)
+ Py_INCREF(item)
+ PyList_SET_ITEM(o, i, item)
cython.operator.preincrement(iter)
+ i += 1
+
return o
#################### set.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass set "std::{{maybe_unordered}}set" [T]:
- void insert(T&)
+ void insert(T&) except +
@cname("{{cname}}")
cdef set[X] {{cname}}(object o) except *:
cdef set[X] s
for item in o:
- s.insert(X_from_py(item))
+ s.insert(item)
return s
@@ -126,8 +152,6 @@
cimport cython
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass cpp_set "std::{{maybe_unordered}}set" [T]:
cppclass const_iterator:
@@ -139,32 +163,23 @@
@cname("{{cname}}")
cdef object {{cname}}(const cpp_set[X]& s):
- o = set()
- cdef cpp_set[X].const_iterator iter = s.begin()
- while iter != s.end():
- o.add(X_to_py(cython.operator.dereference(iter)))
- cython.operator.preincrement(iter)
- return o
+ return {v for v in s}
#################### pair.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass pair "std::pair" [T, U]:
- pair()
- pair(T&, U&)
+ pair() except +
+ pair(T&, U&) except +
@cname("{{cname}}")
cdef pair[X,Y] {{cname}}(object o) except *:
x, y = o
- return pair[X,Y](X_from_py(x), Y_from_py(y))
+ return pair[X,Y](x, y)
#################### pair.to_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass pair "std::pair" [T, U]:
T first
@@ -172,28 +187,30 @@
@cname("{{cname}}")
cdef object {{cname}}(const pair[X,Y]& p):
- return X_to_py(p.first), Y_to_py(p.second)
+ return p.first, p.second
#################### map.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass pair "std::pair" [T, U]:
- pair(T&, U&)
+ pair(T&, U&) except +
cdef cppclass map "std::{{maybe_unordered}}map" [T, U]:
- void insert(pair[T, U]&)
+ void insert(pair[T, U]&) except +
cdef cppclass vector "std::vector" [T]:
pass
+ int PY_MAJOR_VERSION
@cname("{{cname}}")
cdef map[X,Y] {{cname}}(object o) except *:
- cdef dict d = o
cdef map[X,Y] m
- for key, value in d.iteritems():
- m.insert(pair[X,Y](X_from_py(key), Y_from_py(value)))
+ if PY_MAJOR_VERSION < 3:
+ for key, value in o.iteritems():
+ m.insert(pair[X,Y](key, value))
+ else:
+ for key, value in o.items():
+ m.insert(pair[X,Y](key, value))
return m
@@ -203,8 +220,6 @@
cimport cython
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass map "std::{{maybe_unordered}}map" [T, U]:
cppclass value_type:
@@ -224,15 +239,13 @@
cdef map[X,Y].const_iterator iter = s.begin()
while iter != s.end():
key_value = &cython.operator.dereference(iter)
- o[X_to_py(key_value.first)] = Y_to_py(key_value.second)
+ o[key_value.first] = key_value.second
cython.operator.preincrement(iter)
return o
#################### complex.from_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass std_complex "std::complex" [T]:
std_complex()
@@ -246,8 +259,6 @@
#################### complex.to_py ####################
-{{template_type_declarations}}
-
cdef extern from *:
cdef cppclass std_complex "std::complex" [T]:
X real()
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CppSupport.cpp cython-0.20.1+1~202203241016-9537/Cython/Utility/CppSupport.cpp
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CppSupport.cpp 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CppSupport.cpp 2022-03-24 10:16:46.000000000 +0000
@@ -46,3 +46,53 @@
}
}
#endif
+
+/////////////// PythranConversion.proto ///////////////
+
+template
+auto __Pyx_pythran_to_python(T &&value) -> decltype(to_python(
+ typename pythonic::returnable::type>::type>::type{std::forward(value)}))
+{
+ using returnable_type = typename pythonic::returnable::type>::type>::type;
+ return to_python(returnable_type{std::forward(value)});
+}
+
+#define __Pyx_PythranShapeAccessor(x) (pythonic::builtins::getattr(pythonic::types::attr::SHAPE{}, x))
+
+////////////// MoveIfSupported.proto //////////////////
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)
+ // move should be defined for these versions of MSVC, but __cplusplus isn't set usefully
+ #include
+ #define __PYX_STD_MOVE_IF_SUPPORTED(x) std::move(x)
+#else
+ #define __PYX_STD_MOVE_IF_SUPPORTED(x) x
+#endif
+
+////////////// EnumClassDecl.proto //////////////////
+
+#if defined (_MSC_VER)
+ #if _MSC_VER >= 1910
+ #define __PYX_ENUM_CLASS_DECL enum
+ #else
+ #define __PYX_ENUM_CLASS_DECL
+ #endif
+#else
+ #define __PYX_ENUM_CLASS_DECL enum
+#endif
+
+////////////// OptionalLocals.proto ////////////////
+//@proto_block: utility_code_proto_before_types
+
+#if defined(CYTHON_USE_BOOST_OPTIONAL)
+ // fallback mode - std::optional is preferred but this gives
+ // people with a less up-to-date compiler a chance
+ #include
+ #define __Pyx_Optional_Type boost::optional
+#else
+ #include
+ // since std::optional is a C++17 features, a templated using declaration should be safe
+ // (although it could be replaced with a define)
+ template
+ using __Pyx_Optional_Type = std::optional;
+#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/CythonFunction.c cython-0.20.1+1~202203241016-9537/Cython/Utility/CythonFunction.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/CythonFunction.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/CythonFunction.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,17 +1,25 @@
+//////////////////// CythonFunctionShared.proto ////////////////////
-//////////////////// CythonFunction.proto ////////////////////
-#define __Pyx_CyFunction_USED 1
-#include
+#define __Pyx_CyFunction_USED
#define __Pyx_CYFUNCTION_STATICMETHOD 0x01
#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02
#define __Pyx_CYFUNCTION_CCLASS 0x04
+#define __Pyx_CYFUNCTION_COROUTINE 0x08
#define __Pyx_CyFunction_GetClosure(f) \
(((__pyx_CyFunctionObject *) (f))->func_closure)
-#define __Pyx_CyFunction_GetClassObj(f) \
- (((__pyx_CyFunctionObject *) (f))->func_classobj)
+
+#if PY_VERSION_HEX < 0x030900B1
+ #define __Pyx_CyFunction_GetClassObj(f) \
+ (((__pyx_CyFunctionObject *) (f))->func_classobj)
+#else
+ #define __Pyx_CyFunction_GetClassObj(f) \
+ ((PyObject*) ((PyCMethodObject *) (f))->mm_class)
+#endif
+#define __Pyx_CyFunction_SetClassObj(f, classobj) \
+ __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj))
#define __Pyx_CyFunction_Defaults(type, f) \
((type *)(((__pyx_CyFunctionObject *) (f))->defaults))
@@ -20,7 +28,15 @@
typedef struct {
+#if PY_VERSION_HEX < 0x030900B1
PyCFunctionObject func;
+#else
+ // PEP-573: PyCFunctionObject + mm_class
+ PyCMethodObject func;
+#endif
+#if CYTHON_BACKPORT_VECTORCALL
+ __pyx_vectorcallfunc func_vectorcall;
+#endif
#if PY_VERSION_HEX < 0x030500A0
PyObject *func_weakreflist;
#endif
@@ -31,12 +47,14 @@
PyObject *func_globals;
PyObject *func_code;
PyObject *func_closure;
+#if PY_VERSION_HEX < 0x030900B1
// No-args super() class cell
PyObject *func_classobj;
-
+#endif
// Dynamic default args and annotations
void *defaults;
int defaults_pyobjects;
+ size_t defaults_size; // used by FusedFunction for copying defaults
int flags;
// Defaults info
@@ -44,19 +62,26 @@
PyObject *defaults_kwdict; /* Const kwonly defaults dict */
PyObject *(*defaults_getter)(PyObject *);
PyObject *func_annotations; /* function annotations dict */
+
+ // Coroutine marker
+ PyObject *func_is_coroutine;
} __pyx_CyFunctionObject;
+#if !CYTHON_USE_MODULE_STATE
static PyTypeObject *__pyx_CyFunctionType = 0;
+#endif
-#define __Pyx_CyFunction_NewEx(ml, flags, qualname, self, module, globals, code) \
- __Pyx_CyFunction_New(__pyx_CyFunctionType, ml, flags, qualname, self, module, globals, code)
+#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType)
+#define __Pyx_IsCyOrPyCFunction(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type)
+#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType)
-static PyObject *__Pyx_CyFunction_New(PyTypeObject *, PyMethodDef *ml,
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml,
int flags, PyObject* qualname,
- PyObject *self,
+ PyObject *closure,
PyObject *module, PyObject *globals,
PyObject* code);
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj);
static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m,
size_t size,
int pyobjects);
@@ -68,22 +93,51 @@
PyObject *dict);
-static int __pyx_CyFunction_init(void);
+static int __pyx_CyFunction_init(PyObject *module);
-//////////////////// CythonFunction ////////////////////
+#if CYTHON_METH_FASTCALL
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+#if CYTHON_BACKPORT_VECTORCALL
+#define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall)
+#else
+#define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall)
+#endif
+#endif
+
+//////////////////// CythonFunctionShared ////////////////////
//@substitute: naming
-//@requires: CommonTypes.c::FetchCommonType
-////@requires: ObjectHandling.c::PyObjectGetAttrStr
+//@requires: CommonStructures.c::FetchCommonType
+//@requires: ObjectHandling.c::PyMethodNew
+//@requires: ObjectHandling.c::PyVectorcallFastCallDict
+//@requires: ModuleSetupCode.c::IncludeStructmemberH
+//@requires: ObjectHandling.c::PyObjectGetAttrStr
+
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) {
+#if PY_VERSION_HEX < 0x030900B1
+ __Pyx_Py_XDECREF_SET(
+ __Pyx_CyFunction_GetClassObj(f),
+ ((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#else
+ __Pyx_Py_XDECREF_SET(
+ // assigning to "mm_class", which is a "PyTypeObject*"
+ ((PyCMethodObject *) (f))->mm_class,
+ (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#endif
+}
static PyObject *
-__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, CYTHON_UNUSED void *closure)
+__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure)
{
+ CYTHON_UNUSED_VAR(closure);
if (unlikely(op->func_doc == NULL)) {
- if (op->func.m_ml->ml_doc) {
+ if (((PyCFunctionObject*)op)->m_ml->ml_doc) {
#if PY_MAJOR_VERSION >= 3
- op->func_doc = PyUnicode_FromString(op->func.m_ml->ml_doc);
+ op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc);
#else
- op->func_doc = PyString_FromString(op->func.m_ml->ml_doc);
+ op->func_doc = PyString_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc);
#endif
if (unlikely(op->func_doc == NULL))
return NULL;
@@ -97,27 +151,27 @@
}
static int
-__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value)
+__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context)
{
- PyObject *tmp = op->func_doc;
+ CYTHON_UNUSED_VAR(context);
if (value == NULL) {
// Mark as deleted
value = Py_None;
}
Py_INCREF(value);
- op->func_doc = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->func_doc, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context)
{
+ CYTHON_UNUSED_VAR(context);
if (unlikely(op->func_name == NULL)) {
#if PY_MAJOR_VERSION >= 3
- op->func_name = PyUnicode_InternFromString(op->func.m_ml->ml_name);
+ op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name);
#else
- op->func_name = PyString_InternFromString(op->func.m_ml->ml_name);
+ op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name);
#endif
if (unlikely(op->func_name == NULL))
return NULL;
@@ -127,69 +181,55 @@
}
static int
-__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value)
+__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context)
{
- PyObject *tmp;
-
+ CYTHON_UNUSED_VAR(context);
#if PY_MAJOR_VERSION >= 3
- if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ if (unlikely(value == NULL || !PyUnicode_Check(value)))
#else
- if (unlikely(value == NULL || !PyString_Check(value))) {
+ if (unlikely(value == NULL || !PyString_Check(value)))
#endif
+ {
PyErr_SetString(PyExc_TypeError,
"__name__ must be set to a string object");
return -1;
}
- tmp = op->func_name;
Py_INCREF(value);
- op->func_name = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->func_name, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context)
{
+ CYTHON_UNUSED_VAR(context);
Py_INCREF(op->func_qualname);
return op->func_qualname;
}
static int
-__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value)
+__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context)
{
- PyObject *tmp;
-
+ CYTHON_UNUSED_VAR(context);
#if PY_MAJOR_VERSION >= 3
- if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ if (unlikely(value == NULL || !PyUnicode_Check(value)))
#else
- if (unlikely(value == NULL || !PyString_Check(value))) {
+ if (unlikely(value == NULL || !PyString_Check(value)))
#endif
+ {
PyErr_SetString(PyExc_TypeError,
"__qualname__ must be set to a string object");
return -1;
}
- tmp = op->func_qualname;
Py_INCREF(value);
- op->func_qualname = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->func_qualname, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure)
-{
- PyObject *self;
-
- self = m->func_closure;
- if (self == NULL)
- self = Py_None;
- Py_INCREF(self);
- return self;
-}
-
-static PyObject *
-__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context)
{
+ CYTHON_UNUSED_VAR(context);
if (unlikely(op->func_dict == NULL)) {
op->func_dict = PyDict_New();
if (unlikely(op->func_dict == NULL))
@@ -200,10 +240,9 @@
}
static int
-__Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value)
+__Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value, void *context)
{
- PyObject *tmp;
-
+ CYTHON_UNUSED_VAR(context);
if (unlikely(value == NULL)) {
PyErr_SetString(PyExc_TypeError,
"function's dictionary may not be deleted");
@@ -214,31 +253,33 @@
"setting function's dictionary to a non-dict");
return -1;
}
- tmp = op->func_dict;
Py_INCREF(value);
- op->func_dict = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->func_dict, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context)
{
+ CYTHON_UNUSED_VAR(context);
Py_INCREF(op->func_globals);
return op->func_globals;
}
static PyObject *
-__Pyx_CyFunction_get_closure(CYTHON_UNUSED __pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context)
{
+ CYTHON_UNUSED_VAR(op);
+ CYTHON_UNUSED_VAR(context);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
-__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op)
+__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context)
{
PyObject* result = (op->func_code) ? op->func_code : Py_None;
+ CYTHON_UNUSED_VAR(context);
Py_INCREF(result);
return result;
}
@@ -269,29 +310,30 @@
}
static int
-__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value) {
- PyObject* tmp;
+__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
if (!value) {
// del => explicit None to prevent rebuilding
value = Py_None;
- } else if (value != Py_None && !PyTuple_Check(value)) {
+ } else if (unlikely(value != Py_None && !PyTuple_Check(value))) {
PyErr_SetString(PyExc_TypeError,
"__defaults__ must be set to a tuple object");
return -1;
}
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not "
+ "currently affect the values used in function calls", 1);
Py_INCREF(value);
- tmp = op->defaults_tuple;
- op->defaults_tuple = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->defaults_tuple, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op) {
+__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) {
PyObject* result = op->defaults_tuple;
+ CYTHON_UNUSED_VAR(context);
if (unlikely(!result)) {
if (op->defaults_getter) {
- if (__Pyx_CyFunction_init_defaults(op) < 0) return NULL;
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
result = op->defaults_tuple;
} else {
result = Py_None;
@@ -302,29 +344,30 @@
}
static int
-__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value) {
- PyObject* tmp;
+__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
if (!value) {
// del => explicit None to prevent rebuilding
value = Py_None;
- } else if (value != Py_None && !PyDict_Check(value)) {
+ } else if (unlikely(value != Py_None && !PyDict_Check(value))) {
PyErr_SetString(PyExc_TypeError,
"__kwdefaults__ must be set to a dict object");
return -1;
}
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not "
+ "currently affect the values used in function calls", 1);
Py_INCREF(value);
- tmp = op->defaults_kwdict;
- op->defaults_kwdict = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op) {
+__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) {
PyObject* result = op->defaults_kwdict;
+ CYTHON_UNUSED_VAR(context);
if (unlikely(!result)) {
if (op->defaults_getter) {
- if (__Pyx_CyFunction_init_defaults(op) < 0) return NULL;
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
result = op->defaults_kwdict;
} else {
result = Py_None;
@@ -335,25 +378,24 @@
}
static int
-__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value) {
- PyObject* tmp;
+__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
if (!value || value == Py_None) {
value = NULL;
- } else if (!PyDict_Check(value)) {
+ } else if (unlikely(!PyDict_Check(value))) {
PyErr_SetString(PyExc_TypeError,
"__annotations__ must be set to a dict object");
return -1;
}
Py_XINCREF(value);
- tmp = op->func_annotations;
- op->func_annotations = value;
- Py_XDECREF(tmp);
+ __Pyx_Py_XDECREF_SET(op->func_annotations, value);
return 0;
}
static PyObject *
-__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op) {
+__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) {
PyObject* result = op->func_annotations;
+ CYTHON_UNUSED_VAR(context);
if (unlikely(!result)) {
result = PyDict_New();
if (unlikely(!result)) return NULL;
@@ -363,10 +405,44 @@
return result;
}
+static PyObject *
+__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) {
+ int is_coroutine;
+ CYTHON_UNUSED_VAR(context);
+ if (op->func_is_coroutine) {
+ return __Pyx_NewRef(op->func_is_coroutine);
+ }
+
+ is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE;
+#if PY_VERSION_HEX >= 0x03050000
+ if (is_coroutine) {
+ PyObject *module, *fromlist, *marker = PYIDENT("_is_coroutine");
+ fromlist = PyList_New(1);
+ if (unlikely(!fromlist)) return NULL;
+ Py_INCREF(marker);
+ PyList_SET_ITEM(fromlist, 0, marker);
+ module = PyImport_ImportModuleLevelObject(PYIDENT("asyncio.coroutines"), NULL, NULL, fromlist, 0);
+ Py_DECREF(fromlist);
+ if (unlikely(!module)) goto ignore;
+ op->func_is_coroutine = __Pyx_PyObject_GetAttrStr(module, marker);
+ Py_DECREF(module);
+ if (likely(op->func_is_coroutine)) {
+ return __Pyx_NewRef(op->func_is_coroutine);
+ }
+ignore:
+ PyErr_Clear();
+ }
+#endif
+
+ op->func_is_coroutine = __Pyx_PyBool_FromLong(is_coroutine);
+ return __Pyx_NewRef(op->func_is_coroutine);
+}
+
//#if PY_VERSION_HEX >= 0x030400C1
//static PyObject *
-//__Pyx_CyFunction_get_signature(__pyx_CyFunctionObject *op) {
+//__Pyx_CyFunction_get_signature(__pyx_CyFunctionObject *op, void *context) {
// PyObject *inspect_module, *signature_class, *signature;
+// CYTHON_UNUSED_VAR(context);
// // from inspect import Signature
// inspect_module = PyImport_ImportModuleLevelObject(PYIDENT("inspect"), NULL, NULL, NULL, 0);
// if (unlikely(!inspect_module))
@@ -394,7 +470,6 @@
{(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
- {(char *) "__self__", (getter)__Pyx_CyFunction_get_self, 0, 0, 0},
{(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
@@ -407,6 +482,7 @@
{(char *) "__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
{(char *) "__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0},
{(char *) "__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0},
+ {(char *) "_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0},
//#if PY_VERSION_HEX >= 0x030400C1
// {(char *) "__signature__", (getter)__Pyx_CyFunction_get_signature, 0, 0, 0},
//#endif
@@ -414,17 +490,34 @@
};
static PyMemberDef __pyx_CyFunction_members[] = {
- {(char *) "__module__", T_OBJECT, offsetof(__pyx_CyFunctionObject, func.m_module), PY_WRITE_RESTRICTED, 0},
+ {(char *) "__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0},
+#if CYTHON_USE_TYPE_SPECS
+ {(char *) "__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0},
+#if CYTHON_METH_FASTCALL
+#if CYTHON_BACKPORT_VECTORCALL
+ {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0},
+#else
+ {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0},
+#endif
+#endif
+#if PY_VERSION_HEX < 0x030500A0
+ {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0},
+#else
+ {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0},
+#endif
+#endif
{0, 0, 0, 0, 0}
};
static PyObject *
-__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, CYTHON_UNUSED PyObject *args)
+__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args)
{
+ CYTHON_UNUSED_VAR(args);
#if PY_MAJOR_VERSION >= 3
- return PyUnicode_FromString(m->func.m_ml->ml_name);
+ Py_INCREF(m->func_qualname);
+ return m->func_qualname;
#else
- return PyString_FromString(m->func.m_ml->ml_name);
+ return PyString_FromString(((PyCFunctionObject*)m)->m_ml->ml_name);
#endif
}
@@ -437,41 +530,70 @@
#if PY_VERSION_HEX < 0x030500A0
#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist)
#else
-#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func.m_weakreflist)
+#define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist)
#endif
-
-static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, PyObject* qualname,
- PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
- __pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type);
- if (op == NULL)
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+ PyCFunctionObject *cf = (PyCFunctionObject*) op;
+ if (unlikely(op == NULL))
return NULL;
op->flags = flags;
__Pyx_CyFunction_weakreflist(op) = NULL;
- op->func.m_ml = ml;
- op->func.m_self = (PyObject *) op;
+ cf->m_ml = ml;
+ cf->m_self = (PyObject *) op;
Py_XINCREF(closure);
op->func_closure = closure;
Py_XINCREF(module);
- op->func.m_module = module;
+ cf->m_module = module;
op->func_dict = NULL;
op->func_name = NULL;
Py_INCREF(qualname);
op->func_qualname = qualname;
op->func_doc = NULL;
+#if PY_VERSION_HEX < 0x030900B1
op->func_classobj = NULL;
+#else
+ ((PyCMethodObject*)op)->mm_class = NULL;
+#endif
op->func_globals = globals;
Py_INCREF(op->func_globals);
Py_XINCREF(code);
op->func_code = code;
// Dynamic Default args
op->defaults_pyobjects = 0;
+ op->defaults_size = 0;
op->defaults = NULL;
op->defaults_tuple = NULL;
op->defaults_kwdict = NULL;
op->defaults_getter = NULL;
op->func_annotations = NULL;
- PyObject_GC_Track(op);
+ op->func_is_coroutine = NULL;
+#if CYTHON_METH_FASTCALL
+ switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) {
+ case METH_NOARGS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS;
+ break;
+ case METH_O:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O;
+ break;
+ // case METH_FASTCALL is not used
+ case METH_METHOD | METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD;
+ break;
+ case METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS;
+ break;
+ // case METH_VARARGS is not used
+ case METH_VARARGS | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = NULL;
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
+ Py_DECREF(op);
+ return NULL;
+ }
+#endif
return (PyObject *) op;
}
@@ -479,17 +601,26 @@
__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
{
Py_CLEAR(m->func_closure);
- Py_CLEAR(m->func.m_module);
+ Py_CLEAR(((PyCFunctionObject*)m)->m_module);
Py_CLEAR(m->func_dict);
Py_CLEAR(m->func_name);
Py_CLEAR(m->func_qualname);
Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_globals);
Py_CLEAR(m->func_code);
- Py_CLEAR(m->func_classobj);
+#if PY_VERSION_HEX < 0x030900B1
+ Py_CLEAR(__Pyx_CyFunction_GetClassObj(m));
+#else
+ {
+ PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class;
+ ((PyCMethodObject *) (m))->mm_class = NULL;
+ Py_XDECREF(cls);
+ }
+#endif
Py_CLEAR(m->defaults_tuple);
Py_CLEAR(m->defaults_kwdict);
Py_CLEAR(m->func_annotations);
+ Py_CLEAR(m->func_is_coroutine);
if (m->defaults) {
PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
@@ -505,28 +636,34 @@
return 0;
}
-static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
+static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m)
{
- PyObject_GC_UnTrack(m);
if (__Pyx_CyFunction_weakreflist(m) != NULL)
PyObject_ClearWeakRefs((PyObject *) m);
__Pyx_CyFunction_clear(m);
- PyObject_GC_Del(m);
+ __Pyx_PyHeapTypeObject_GC_Del(m);
+}
+
+static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
+{
+ PyObject_GC_UnTrack(m);
+ __Pyx__CyFunction_dealloc(m);
}
static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg)
{
Py_VISIT(m->func_closure);
- Py_VISIT(m->func.m_module);
+ Py_VISIT(((PyCFunctionObject*)m)->m_module);
Py_VISIT(m->func_dict);
Py_VISIT(m->func_name);
Py_VISIT(m->func_qualname);
Py_VISIT(m->func_doc);
Py_VISIT(m->func_globals);
Py_VISIT(m->func_code);
- Py_VISIT(m->func_classobj);
+ Py_VISIT(__Pyx_CyFunction_GetClassObj(m));
Py_VISIT(m->defaults_tuple);
Py_VISIT(m->defaults_kwdict);
+ Py_VISIT(m->func_is_coroutine);
if (m->defaults) {
PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
@@ -539,26 +676,6 @@
return 0;
}
-static PyObject *__Pyx_CyFunction_descr_get(PyObject *func, PyObject *obj, PyObject *type)
-{
- __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
-
- if (m->flags & __Pyx_CYFUNCTION_STATICMETHOD) {
- Py_INCREF(func);
- return func;
- }
-
- if (m->flags & __Pyx_CYFUNCTION_CLASSMETHOD) {
- if (type == NULL)
- type = (PyObject *)(Py_TYPE(obj));
- return __Pyx_PyMethod_New(func, type, (PyObject *)(Py_TYPE(type)));
- }
-
- if (obj == Py_None)
- obj = NULL;
- return __Pyx_PyMethod_New(func, obj, type);
-}
-
static PyObject*
__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
{
@@ -583,7 +700,7 @@
return (*meth)(self, arg);
break;
case METH_VARARGS | METH_KEYWORDS:
- return (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
+ return (*(PyCFunctionWithKeywords)(void*)meth)(self, arg, kw);
case METH_NOARGS:
if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
size = PyTuple_GET_SIZE(arg);
@@ -599,10 +716,16 @@
if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
size = PyTuple_GET_SIZE(arg);
if (likely(size == 1)) {
- PyObject *result, *arg0 = PySequence_ITEM(arg, 0);
- if (unlikely(!arg0)) return NULL;
+ PyObject *result, *arg0;
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ arg0 = PyTuple_GET_ITEM(arg, 0);
+ #else
+ arg0 = PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL;
+ #endif
result = (*meth)(self, arg0);
+ #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
Py_DECREF(arg0);
+ #endif
return result;
}
PyErr_Format(PyExc_TypeError,
@@ -612,10 +735,7 @@
}
break;
default:
- PyErr_SetString(PyExc_SystemError, "Bad call flags in "
- "__Pyx_CyFunction_Call. METH_OLDARGS is no "
- "longer supported!");
-
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
return NULL;
}
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
@@ -630,6 +750,22 @@
static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) {
PyObject *result;
__pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
+
+#if CYTHON_METH_FASTCALL
+ // Prefer vectorcall if available. This is not the typical case, as
+ // CPython would normally use vectorcall directly instead of tp_call.
+ __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc);
+ if (vc) {
+#if CYTHON_ASSUME_SAFE_MACROS
+ return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw);
+#else
+ // avoid unused function warning
+ (void) &__Pyx_PyVectorcall_FastCallDict;
+ return PyVectorcall_Call(func, args, kw);
+#endif
+ }
+#endif
+
if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
Py_ssize_t argc;
PyObject *new_args;
@@ -655,19 +791,198 @@
return result;
}
+#if CYTHON_METH_FASTCALL
+// Check that kwnames is empty (if you want to allow keyword arguments,
+// simply pass kwnames=NULL) and figure out what to do with "self".
+// Return value:
+// 1: self = args[0]
+// 0: self = cyfunc->func.m_self
+// -1: error
+static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames)
+{
+ int ret = 0;
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
+ if (unlikely(nargs < 1)) {
+ PyErr_Format(PyExc_TypeError, "%.200s() needs an argument",
+ ((PyCFunctionObject*)cyfunc)->m_ml->ml_name);
+ return -1;
+ }
+ ret = 1;
+ }
+ if (unlikely(kwnames) && unlikely(PyTuple_GET_SIZE(kwnames))) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no keyword arguments", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name);
+ return -1;
+ }
+ return ret;
+}
+
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml;
+#if CYTHON_BACKPORT_VECTORCALL
+ Py_ssize_t nargs = (Py_ssize_t)nargsf;
+#else
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+#endif
+ PyObject *self;
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+ break;
+ default:
+ return NULL;
+ }
+
+ if (unlikely(nargs != 0)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ def->ml_name, nargs);
+ return NULL;
+ }
+ return def->ml_meth(self, NULL);
+}
+
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml;
+#if CYTHON_BACKPORT_VECTORCALL
+ Py_ssize_t nargs = (Py_ssize_t)nargsf;
+#else
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+#endif
+ PyObject *self;
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+ break;
+ default:
+ return NULL;
+ }
+
+ if (unlikely(nargs != 1)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ def->ml_name, nargs);
+ return NULL;
+ }
+ return def->ml_meth(self, args[0]);
+}
+
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml;
+#if CYTHON_BACKPORT_VECTORCALL
+ Py_ssize_t nargs = (Py_ssize_t)nargsf;
+#else
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+#endif
+ PyObject *self;
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+ break;
+ default:
+ return NULL;
+ }
+
+ return ((_PyCFunctionFastWithKeywords)(void(*)(void))def->ml_meth)(self, args, nargs, kwnames);
+}
+
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml;
+ PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc);
+#if CYTHON_BACKPORT_VECTORCALL
+ Py_ssize_t nargs = (Py_ssize_t)nargsf;
+#else
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+#endif
+ PyObject *self;
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+ break;
+ default:
+ return NULL;
+ }
+
+ return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, nargs, kwnames);
+}
+#endif
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_CyFunctionType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc},
+ {Py_tp_repr, (void *)__Pyx_CyFunction_repr},
+ {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod},
+ {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse},
+ {Py_tp_clear, (void *)__Pyx_CyFunction_clear},
+ {Py_tp_methods, (void *)__pyx_CyFunction_methods},
+ {Py_tp_members, (void *)__pyx_CyFunction_members},
+ {Py_tp_getset, (void *)__pyx_CyFunction_getsets},
+ {Py_tp_descr_get, (void *)__Pyx_PyMethod_New},
+ {0, 0},
+};
+
+static PyType_Spec __pyx_CyFunctionType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "cython_function_or_method",
+ sizeof(__pyx_CyFunctionObject),
+ 0,
+#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR
+ Py_TPFLAGS_METHOD_DESCRIPTOR |
+#endif
+#if (defined(_Py_TPFLAGS_HAVE_VECTORCALL) && CYTHON_METH_FASTCALL)
+ _Py_TPFLAGS_HAVE_VECTORCALL |
+#endif
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ __pyx_CyFunctionType_slots
+};
+#else /* CYTHON_USE_TYPE_SPECS */
+
static PyTypeObject __pyx_CyFunctionType_type = {
PyVarObject_HEAD_INIT(0, 0)
- "cython_function_or_method", /*tp_name*/
+ __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", /*tp_name*/
sizeof(__pyx_CyFunctionObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __Pyx_CyFunction_dealloc, /*tp_dealloc*/
+#if !CYTHON_METH_FASTCALL
0, /*tp_print*/
+#elif CYTHON_BACKPORT_VECTORCALL
+ (printfunc)offsetof(__pyx_CyFunctionObject, func_vectorcall), /*tp_vectorcall_offset backported into tp_print*/
+#else
+ offsetof(PyCFunctionObject, vectorcall), /*tp_vectorcall_offset*/
+#endif
0, /*tp_getattr*/
0, /*tp_setattr*/
#if PY_MAJOR_VERSION < 3
0, /*tp_compare*/
#else
- 0, /*reserved*/
+ 0, /*tp_as_async*/
#endif
(reprfunc) __Pyx_CyFunction_repr, /*tp_repr*/
0, /*tp_as_number*/
@@ -679,7 +994,13 @@
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR
+ Py_TPFLAGS_METHOD_DESCRIPTOR |
+#endif
+#ifdef _Py_TPFLAGS_HAVE_VECTORCALL
+ _Py_TPFLAGS_HAVE_VECTORCALL |
+#endif
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
(traverseproc) __Pyx_CyFunction_traverse, /*tp_traverse*/
(inquiry) __Pyx_CyFunction_clear, /*tp_clear*/
@@ -696,7 +1017,7 @@
__pyx_CyFunction_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
- __Pyx_CyFunction_descr_get, /*tp_descr_get*/
+ __Pyx_PyMethod_New, /*tp_descr_get*/
0, /*tp_descr_set*/
offsetof(__pyx_CyFunctionObject, func_dict),/*tp_dictoffset*/
0, /*tp_init*/
@@ -714,12 +1035,27 @@
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
};
+#endif /* CYTHON_USE_TYPE_SPECS */
-static int __pyx_CyFunction_init(void) {
+static int __pyx_CyFunction_init(PyObject *module) {
+#if CYTHON_USE_TYPE_SPECS
+ __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CyFunctionType_spec, NULL);
+#else
+ (void) module;
__pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type);
- if (__pyx_CyFunctionType == NULL) {
+#endif
+ if (unlikely(__pyx_CyFunctionType == NULL)) {
return -1;
}
return 0;
@@ -729,10 +1065,11 @@
__pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
m->defaults = PyObject_Malloc(size);
- if (!m->defaults)
+ if (unlikely(!m->defaults))
return PyErr_NoMemory();
memset(m->defaults, 0, size);
m->defaults_pyobjects = pyobjects;
+ m->defaults_size = size;
return m->defaults;
}
@@ -754,13 +1091,38 @@
Py_INCREF(dict);
}
+
+//////////////////// CythonFunction.proto ////////////////////
+
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml,
+ int flags, PyObject* qualname,
+ PyObject *closure,
+ PyObject *module, PyObject *globals,
+ PyObject* code);
+
+//////////////////// CythonFunction ////////////////////
+//@requires: CythonFunctionShared
+
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+ PyObject *op = __Pyx_CyFunction_Init(
+ PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType),
+ ml, flags, qualname, closure, module, globals, code
+ );
+ if (likely(op)) {
+ PyObject_GC_Track(op);
+ }
+ return op;
+}
+
+
//////////////////// CyFunctionClassCell.proto ////////////////////
-static CYTHON_INLINE int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj);
+static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj);/*proto*/
//////////////////// CyFunctionClassCell ////////////////////
-//@requires: CythonFunction
+//@requires: CythonFunctionShared
-static CYTHON_INLINE int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) {
+static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) {
Py_ssize_t i, count = PyList_GET_SIZE(cyfunctions);
for (i = 0; i < count; i++) {
@@ -772,8 +1134,7 @@
if (unlikely(!m))
return -1;
#endif
- Py_INCREF(classobj);
- m->func_classobj = classobj;
+ __Pyx_CyFunction_SetClassObj(m, classobj);
#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
Py_DECREF((PyObject*)m);
#endif
@@ -781,52 +1142,58 @@
return 0;
}
+
//////////////////// FusedFunction.proto ////////////////////
+
typedef struct {
__pyx_CyFunctionObject func;
PyObject *__signatures__;
- PyObject *type;
PyObject *self;
} __pyx_FusedFunctionObject;
-#define __pyx_FusedFunction_NewEx(ml, flags, qualname, self, module, globals, code) \
- __pyx_FusedFunction_New(__pyx_FusedFunctionType, ml, flags, qualname, self, module, globals, code)
-static PyObject *__pyx_FusedFunction_New(PyTypeObject *type,
- PyMethodDef *ml, int flags,
- PyObject *qualname, PyObject *self,
+static PyObject *__pyx_FusedFunction_New(PyMethodDef *ml, int flags,
+ PyObject *qualname, PyObject *closure,
PyObject *module, PyObject *globals,
PyObject *code);
static int __pyx_FusedFunction_clear(__pyx_FusedFunctionObject *self);
+#if !CYTHON_USE_MODULE_STATE
static PyTypeObject *__pyx_FusedFunctionType = NULL;
-static int __pyx_FusedFunction_init(void);
+#endif
+static int __pyx_FusedFunction_init(PyObject *module);
#define __Pyx_FusedFunction_USED
//////////////////// FusedFunction ////////////////////
-//@requires: CythonFunction
+//@requires: CythonFunctionShared
static PyObject *
-__pyx_FusedFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags,
- PyObject *qualname, PyObject *self,
+__pyx_FusedFunction_New(PyMethodDef *ml, int flags,
+ PyObject *qualname, PyObject *closure,
PyObject *module, PyObject *globals,
PyObject *code)
{
- __pyx_FusedFunctionObject *fusedfunc =
- (__pyx_FusedFunctionObject *) __Pyx_CyFunction_New(type, ml, flags, qualname,
- self, module, globals, code);
- if (!fusedfunc)
- return NULL;
-
- fusedfunc->__signatures__ = NULL;
- fusedfunc->type = NULL;
- fusedfunc->self = NULL;
- return (PyObject *) fusedfunc;
+ PyObject *op = __Pyx_CyFunction_Init(
+ // __pyx_CyFunctionObject is correct below since that's the cast that we want.
+ PyObject_GC_New(__pyx_CyFunctionObject, __pyx_FusedFunctionType),
+ ml, flags, qualname, closure, module, globals, code
+ );
+ if (likely(op)) {
+ __pyx_FusedFunctionObject *fusedfunc = (__pyx_FusedFunctionObject *) op;
+ fusedfunc->__signatures__ = NULL;
+ fusedfunc->self = NULL;
+ PyObject_GC_Track(op);
+ }
+ return op;
}
-static void __pyx_FusedFunction_dealloc(__pyx_FusedFunctionObject *self) {
- __pyx_FusedFunction_clear(self);
- __pyx_FusedFunctionType->tp_free((PyObject *) self);
+static void
+__pyx_FusedFunction_dealloc(__pyx_FusedFunctionObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_CLEAR(self->self);
+ Py_CLEAR(self->__signatures__);
+ __Pyx__CyFunction_dealloc((__pyx_CyFunctionObject *) self);
}
static int
@@ -835,7 +1202,6 @@
void *arg)
{
Py_VISIT(self->self);
- Py_VISIT(self->type);
Py_VISIT(self->__signatures__);
return __Pyx_CyFunction_traverse((__pyx_CyFunctionObject *) self, visit, arg);
}
@@ -844,7 +1210,6 @@
__pyx_FusedFunction_clear(__pyx_FusedFunctionObject *self)
{
Py_CLEAR(self->self);
- Py_CLEAR(self->type);
Py_CLEAR(self->__signatures__);
return __Pyx_CyFunction_clear((__pyx_CyFunctionObject *) self);
}
@@ -866,7 +1231,16 @@
if (obj == Py_None)
obj = NULL;
- meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx(
+ if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
+ obj = type;
+
+ if (obj == NULL) {
+ // We aren't actually binding to anything, save the effort of rebinding
+ Py_INCREF(self);
+ return self;
+ }
+
+ meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_New(
((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags,
((__pyx_CyFunctionObject *) func)->func_qualname,
@@ -874,24 +1248,38 @@
((PyCFunctionObject *) func)->m_module,
((__pyx_CyFunctionObject *) func)->func_globals,
((__pyx_CyFunctionObject *) func)->func_code);
- if (!meth)
+ if (unlikely(!meth))
return NULL;
- Py_XINCREF(func->func.func_classobj);
- meth->func.func_classobj = func->func.func_classobj;
+ // defaults needs copying fully rather than just copying the pointer
+ // since otherwise it will be freed on destruction of meth despite
+ // belonging to func rather than meth
+ if (func->func.defaults) {
+ PyObject **pydefaults;
+ int i;
+
+ if (unlikely(!__Pyx_CyFunction_InitDefaults(
+ (PyObject*)meth,
+ func->func.defaults_size,
+ func->func.defaults_pyobjects))) {
+ Py_XDECREF((PyObject*)meth);
+ return NULL;
+ }
+ memcpy(meth->func.defaults, func->func.defaults, func->func.defaults_size);
+
+ pydefaults = __Pyx_CyFunction_Defaults(PyObject *, meth);
+ for (i = 0; i < meth->func.defaults_pyobjects; i++)
+ Py_XINCREF(pydefaults[i]);
+ }
+
+ __Pyx_CyFunction_SetClassObj(meth, __Pyx_CyFunction_GetClassObj(func));
Py_XINCREF(func->__signatures__);
meth->__signatures__ = func->__signatures__;
- Py_XINCREF(type);
- meth->type = type;
-
Py_XINCREF(func->func.defaults_tuple);
meth->func.defaults_tuple = func->func.defaults_tuple;
- if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
- obj = type;
-
Py_XINCREF(obj);
meth->self = obj;
@@ -899,12 +1287,18 @@
}
static PyObject *
-_obj_to_str(PyObject *obj)
+_obj_to_string(PyObject *obj)
{
- if (PyType_Check(obj))
+ if (PyUnicode_CheckExact(obj))
+ return __Pyx_NewRef(obj);
+#if PY_MAJOR_VERSION == 2
+ else if (PyString_Check(obj))
+ return PyUnicode_FromEncodedObject(obj, NULL, "strict");
+#endif
+ else if (PyType_Check(obj))
return PyObject_GetAttr(obj, PYIDENT("__name__"));
else
- return PyObject_Str(obj);
+ return PyObject_Unicode(obj);
}
static PyObject *
@@ -914,64 +1308,55 @@
PyObject *unbound_result_func;
PyObject *result_func = NULL;
- if (self->__signatures__ == NULL) {
+ if (unlikely(self->__signatures__ == NULL)) {
PyErr_SetString(PyExc_TypeError, "Function is not fused");
return NULL;
}
if (PyTuple_Check(idx)) {
- PyObject *list = PyList_New(0);
Py_ssize_t n = PyTuple_GET_SIZE(idx);
- PyObject *string = NULL;
- PyObject *sep = NULL;
+ PyObject *list = PyList_New(n);
int i;
- if (!list)
+ if (unlikely(!list))
return NULL;
for (i = 0; i < n; i++) {
+ PyObject *string;
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
PyObject *item = PyTuple_GET_ITEM(idx, i);
#else
- PyObject *item = PySequence_ITEM(idx, i);
+ PyObject *item = PySequence_ITEM(idx, i); if (unlikely(!item)) goto __pyx_err;
#endif
- string = _obj_to_str(item);
+ string = _obj_to_string(item);
#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
Py_DECREF(item);
#endif
- if (!string || PyList_Append(list, string) < 0)
- goto __pyx_err;
-
- Py_DECREF(string);
+ if (unlikely(!string)) goto __pyx_err;
+ PyList_SET_ITEM(list, i, string);
}
- sep = PyUnicode_FromString("|");
- if (sep)
- signature = PyUnicode_Join(sep, list);
-__pyx_err:
-;
+ signature = PyUnicode_Join(PYUNICODE("|"), list);
+__pyx_err:;
Py_DECREF(list);
- Py_XDECREF(sep);
} else {
- signature = _obj_to_str(idx);
+ signature = _obj_to_string(idx);
}
- if (!signature)
+ if (unlikely(!signature))
return NULL;
unbound_result_func = PyObject_GetItem(self->__signatures__, signature);
- if (unbound_result_func) {
- if (self->self || self->type) {
+ if (likely(unbound_result_func)) {
+ if (self->self) {
__pyx_FusedFunctionObject *unbound = (__pyx_FusedFunctionObject *) unbound_result_func;
// TODO: move this to InitClassCell
- Py_CLEAR(unbound->func.func_classobj);
- Py_XINCREF(self->func.func_classobj);
- unbound->func.func_classobj = self->func.func_classobj;
+ __Pyx_CyFunction_SetClassObj(unbound, __Pyx_CyFunction_GetClassObj(self));
result_func = __pyx_FusedFunction_descr_get(unbound_result_func,
- self->self, self->type);
+ self->self, self->self);
} else {
result_func = unbound_result_func;
Py_INCREF(result_func);
@@ -991,7 +1376,7 @@
int static_specialized = (cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD &&
!((__pyx_FusedFunctionObject *) func)->__signatures__);
- if (cyfunc->flags & __Pyx_CYFUNCTION_CCLASS && !static_specialized) {
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !static_specialized) {
return __Pyx_CyFunction_CallAsMethod(func, args, kw);
} else {
return __Pyx_CyFunction_Call(func, args, kw);
@@ -1012,23 +1397,21 @@
PyObject *new_args = NULL;
__pyx_FusedFunctionObject *new_func = NULL;
PyObject *result = NULL;
- PyObject *self = NULL;
int is_staticmethod = binding_func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD;
- int is_classmethod = binding_func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD;
if (binding_func->self) {
// Bound method call, put 'self' in the args tuple
+ PyObject *self;
Py_ssize_t i;
new_args = PyTuple_New(argc + 1);
- if (!new_args)
+ if (unlikely(!new_args))
return NULL;
self = binding_func->self;
-#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
- Py_INCREF(self);
-#endif
+
Py_INCREF(self);
PyTuple_SET_ITEM(new_args, 0, self);
+ self = NULL;
for (i = 0; i < argc; i++) {
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
@@ -1041,36 +1424,8 @@
}
args = new_args;
- } else if (binding_func->type) {
- // Unbound method call
- if (argc < 1) {
- PyErr_SetString(PyExc_TypeError, "Need at least one argument, 0 given.");
- return NULL;
- }
-#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
- self = PyTuple_GET_ITEM(args, 0);
-#else
- self = PySequence_ITEM(args, 0); if (unlikely(!self)) return NULL;
-#endif
}
- if (self && !is_classmethod && !is_staticmethod) {
- int is_instance = PyObject_IsInstance(self, binding_func->type);
- if (unlikely(!is_instance)) {
- PyErr_Format(PyExc_TypeError,
- "First argument should be of type %.200s, got %.200s.",
- ((PyTypeObject *) binding_func->type)->tp_name,
- self->ob_type->tp_name);
- goto bad;
- } else if (unlikely(is_instance == -1)) {
- goto bad;
- }
- }
-#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
- Py_XDECREF(self);
- self = NULL;
-#endif
-
if (binding_func->__signatures__) {
PyObject *tup;
if (is_staticmethod && binding_func->func.flags & __Pyx_CYFUNCTION_CCLASS) {
@@ -1093,23 +1448,31 @@
if (unlikely(!new_func))
goto bad;
- Py_XINCREF(binding_func->func.func_classobj);
- Py_CLEAR(new_func->func.func_classobj);
- new_func->func.func_classobj = binding_func->func.func_classobj;
+ __Pyx_CyFunction_SetClassObj(new_func, __Pyx_CyFunction_GetClassObj(binding_func));
func = (PyObject *) new_func;
}
result = __pyx_FusedFunction_callfunction(func, args, kw);
bad:
-#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
- Py_XDECREF(self);
-#endif
Py_XDECREF(new_args);
Py_XDECREF((PyObject *) new_func);
return result;
}
+static PyObject *
+__Pyx_FusedFunction_get_self(__pyx_FusedFunctionObject *m, void *closure)
+{
+ PyObject *self = m->self;
+ CYTHON_UNUSED_VAR(closure);
+ if (unlikely(!self)) {
+ PyErr_SetString(PyExc_AttributeError, "'function' object has no attribute '__self__'");
+ } else {
+ Py_INCREF(self);
+ }
+ return self;
+}
+
static PyMemberDef __pyx_FusedFunction_members[] = {
{(char *) "__signatures__",
T_OBJECT,
@@ -1119,6 +1482,38 @@
{0, 0, 0, 0, 0},
};
+static PyGetSetDef __pyx_FusedFunction_getsets[] = {
+ {(char *) "__self__", (getter)__Pyx_FusedFunction_get_self, 0, 0, 0},
+ // __doc__ is None for the fused function type, but we need it to be
+ // a descriptor for the instance's __doc__, so rebuild the descriptor in our subclass
+ // (all other descriptors are inherited)
+ {(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_FusedFunctionType_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_FusedFunction_dealloc},
+ {Py_tp_call, (void *)__pyx_FusedFunction_call},
+ {Py_tp_traverse, (void *)__pyx_FusedFunction_traverse},
+ {Py_tp_clear, (void *)__pyx_FusedFunction_clear},
+ {Py_tp_members, (void *)__pyx_FusedFunction_members},
+ {Py_tp_getset, (void *)__pyx_FusedFunction_getsets},
+ {Py_tp_descr_get, (void *)__pyx_FusedFunction_descr_get},
+ {Py_mp_subscript, (void *)__pyx_FusedFunction_getitem},
+ {0, 0},
+};
+
+static PyType_Spec __pyx_FusedFunctionType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "fused_cython_function",
+ sizeof(__pyx_FusedFunctionObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ __pyx_FusedFunctionType_slots
+};
+
+#else /* !CYTHON_USE_TYPE_SPECS */
+
static PyMappingMethods __pyx_FusedFunction_mapping_methods = {
0,
(binaryfunc) __pyx_FusedFunction_getitem,
@@ -1127,7 +1522,7 @@
static PyTypeObject __pyx_FusedFunctionType_type = {
PyVarObject_HEAD_INIT(0, 0)
- "fused_cython_function", /*tp_name*/
+ __PYX_TYPE_MODULE_PREFIX "fused_cython_function", /*tp_name*/
sizeof(__pyx_FusedFunctionObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __pyx_FusedFunction_dealloc, /*tp_dealloc*/
@@ -1137,7 +1532,7 @@
#if PY_MAJOR_VERSION < 3
0, /*tp_compare*/
#else
- 0, /*reserved*/
+ 0, /*tp_as_async*/
#endif
0, /*tp_repr*/
0, /*tp_as_number*/
@@ -1159,9 +1554,8 @@
0, /*tp_iternext*/
0, /*tp_methods*/
__pyx_FusedFunction_members, /*tp_members*/
- // __doc__ is None for the fused function type, but we need it to be
- // a descriptor for the instance's __doc__, so rebuild descriptors in our subclass
- __pyx_CyFunction_getsets, /*tp_getset*/
+ __pyx_FusedFunction_getsets, /*tp_getset*/
+ // NOTE: tp_base may be changed later during module initialisation when importing CyFunction across modules.
&__pyx_CyFunctionType_type, /*tp_base*/
0, /*tp_dict*/
__pyx_FusedFunction_descr_get, /*tp_descr_get*/
@@ -1182,11 +1576,33 @@
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
+#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)
+ 0, /*tp_vectorcall*/
+#endif
+#if PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000
+ 0, /*tp_print*/
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000
+ 0, /*tp_pypy_flags*/
+#endif
};
+#endif
-static int __pyx_FusedFunction_init(void) {
+static int __pyx_FusedFunction_init(PyObject *module) {
+#if CYTHON_USE_TYPE_SPECS
+ PyObject *bases = PyTuple_Pack(1, __pyx_CyFunctionType);
+ if (unlikely(!bases)) {
+ return -1;
+ }
+ __pyx_FusedFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_FusedFunctionType_spec, bases);
+ Py_DECREF(bases);
+#else
+ (void) module;
+ // Set base from __Pyx_FetchCommonTypeFromSpec, in case it's different from the local static value.
+ __pyx_FusedFunctionType_type.tp_base = __pyx_CyFunctionType;
__pyx_FusedFunctionType = __Pyx_FetchCommonType(&__pyx_FusedFunctionType_type);
- if (__pyx_FusedFunctionType == NULL) {
+#endif
+ if (unlikely(__pyx_FusedFunctionType == NULL)) {
return -1;
}
return 0;
@@ -1195,31 +1611,36 @@
//////////////////// ClassMethod.proto ////////////////////
#include "descrobject.h"
-static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
+static CYTHON_UNUSED PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
//////////////////// ClassMethod ////////////////////
static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
-#if CYTHON_COMPILING_IN_PYPY
+#if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM <= 0x05080000
if (PyObject_TypeCheck(method, &PyWrapperDescr_Type)) {
// cdef classes
return PyClassMethod_New(method);
}
#else
-#if CYTHON_COMPILING_IN_PYSTON
- // special C-API function only in Pyston
- if (PyMethodDescr_Check(method)) {
+#if CYTHON_COMPILING_IN_PYPY
+ // special C-API function only in PyPy >= 5.9
+ if (PyMethodDescr_Check(method))
#else
- // It appears that PyMethodDescr_Type is not exposed anywhere in the CPython C-API
+ #if PY_MAJOR_VERSION == 2
+ // PyMethodDescr_Type is not exposed in the CPython C-API in Py2.
static PyTypeObject *methoddescr_type = NULL;
- if (methoddescr_type == NULL) {
+ if (unlikely(methoddescr_type == NULL)) {
PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append");
- if (!meth) return NULL;
+ if (unlikely(!meth)) return NULL;
methoddescr_type = Py_TYPE(meth);
Py_DECREF(meth);
}
- if (PyObject_TypeCheck(method, methoddescr_type)) {
+ #else
+ PyTypeObject *methoddescr_type = &PyMethodDescr_Type;
+ #endif
+ if (__Pyx_TypeCheck(method, methoddescr_type))
#endif
+ {
// cdef classes
PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
#if PY_VERSION_HEX < 0x03020000
@@ -1234,16 +1655,7 @@
// python classes
return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
}
- else if (PyCFunction_Check(method)) {
- return PyClassMethod_New(method);
- }
-#ifdef __Pyx_CyFunction_USED
- else if (PyObject_TypeCheck(method, __pyx_CyFunctionType)) {
+ else {
return PyClassMethod_New(method);
}
-#endif
- PyErr_SetString(PyExc_TypeError,
- "Class-level classmethod() can only be called on "
- "a method_descriptor or instance method.");
- return NULL;
}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Dataclasses.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Dataclasses.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Dataclasses.c 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Dataclasses.c 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,78 @@
+///////////////////// ModuleLoader.proto //////////////////////////
+
+static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback_code); /* proto */
+
+//////////////////// ModuleLoader ///////////////////////
+//@requires: CommonStructures.c::FetchSharedCythonModule
+
+static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback_code) {
+ // We want to be able to use the contents of the standard library dataclasses module where available.
+ // If those objects aren't available (due to Python version) then a simple fallback is substituted
+ // instead, which largely just fails with a not-implemented error.
+ //
+ // The fallbacks are placed in the "shared abi module" as a convenient internal place to
+ // store them
+
+ PyObject *shared_abi_module = 0, *module = 0;
+
+ shared_abi_module = __Pyx_FetchSharedCythonABIModule();
+ if (!shared_abi_module) return NULL;
+
+ if (PyObject_HasAttrString(shared_abi_module, name)) {
+ PyObject* result = PyObject_GetAttrString(shared_abi_module, name);
+ Py_DECREF(shared_abi_module);
+ return result;
+ }
+
+ // the best and simplest case is simply to defer to the standard library (if available)
+ module = PyImport_ImportModule(name);
+ if (!module) {
+ PyObject *localDict, *runValue, *builtins, *modulename;
+ if (!PyErr_ExceptionMatches(PyExc_ImportError)) goto bad;
+ PyErr_Clear(); // this is reasonably likely (especially on older versions of Python)
+#if PY_MAJOR_VERSION < 3
+ modulename = PyBytes_FromFormat("_cython_" CYTHON_ABI ".%s", name);
+#else
+ modulename = PyUnicode_FromFormat("_cython_" CYTHON_ABI ".%s", name);
+#endif
+ if (!modulename) goto bad;
+#if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_CPYTHON
+ module = PyImport_AddModuleObject(modulename); // borrowed
+#else
+ module = PyImport_AddModule(PyBytes_AsString(modulename)); // borrowed
+#endif
+ Py_DECREF(modulename);
+ if (!module) goto bad;
+ Py_INCREF(module);
+ if (PyObject_SetAttrString(shared_abi_module, name, module) < 0) goto bad;
+ localDict = PyModule_GetDict(module); // borrowed
+ if (!localDict) goto bad;
+ builtins = PyEval_GetBuiltins(); // borrowed
+ if (!builtins) goto bad;
+ if (PyDict_SetItemString(localDict, "__builtins__", builtins) <0) goto bad;
+
+ runValue = PyRun_String(fallback_code, Py_file_input, localDict, localDict);
+ if (!runValue) goto bad;
+ Py_DECREF(runValue);
+ }
+ goto shared_cleanup;
+
+ bad:
+ Py_CLEAR(module);
+ shared_cleanup:
+ Py_XDECREF(shared_abi_module);
+ return module;
+}
+
+///////////////////// SpecificModuleLoader.proto //////////////////////
+//@substitute: tempita
+
+static PyObject* __Pyx_Load_{{cname}}_Module(void); /* proto */
+
+
+//////////////////// SpecificModuleLoader ///////////////////////
+//@requires: ModuleLoader
+
+static PyObject* __Pyx_Load_{{cname}}_Module(void) {
+ return __Pyx_LoadInternalModule("{{cname}}", {{py_code}});
+}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Dataclasses.py cython-0.20.1+1~202203241016-9537/Cython/Utility/Dataclasses.py
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Dataclasses.py 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Dataclasses.py 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,106 @@
+################### Dataclasses_fallback ###############################
+
+# This is the fallback dataclass code if the stdlib module isn't available.
+# It defines enough of the support types to be used with cdef classes
+# and to fail if used on regular types.
+
+# (Intended to be included as py code - not compiled)
+
+from collections import namedtuple
+try:
+ from types import MappingProxyType
+except ImportError:
+ # mutable fallback if unavailable
+ MappingProxyType = lambda x: x
+
+class _MISSING_TYPE(object):
+ pass
+MISSING = _MISSING_TYPE()
+
+_DataclassParams = namedtuple('_DataclassParams',
+ ["init", "repr", "eq", "order", "unsafe_hash", "frozen"])
+class Field(object):
+ __slots__ = ('name',
+ 'type',
+ 'default',
+ 'default_factory',
+ 'repr',
+ 'hash',
+ 'init',
+ 'compare',
+ 'metadata',
+ '_field_type', # Private: not to be used by user code.
+ )
+
+ def __init__(self, default, default_factory, init, repr, hash, compare,
+ metadata):
+ self.name = None
+ self.type = None
+ self.default = default
+ self.default_factory = default_factory
+ self.init = init
+ self.repr = repr
+ self.hash = hash
+ self.compare = compare
+ # Be aware that if MappingProxyType is unavailable (i.e. py2?) then we
+ # don't enforce non-mutability that the real module does
+ self.metadata = (MappingProxyType({})
+ if metadata is None else
+ MappingProxyType(metadata))
+ self._field_type = None
+
+ def __repr__(self):
+ return ('Field('
+ 'name={0!r},'
+ 'type={1!r},'
+ 'default={2!r},'
+ 'default_factory={3!r},'
+ 'init={4!r},'
+ 'repr={5!r},'
+ 'hash={6!r},'
+ 'compare={7!r},'
+ 'metadata={8!r},'
+ ')'.format(self.name, self.type, self.default,
+ self.default_factory, self.init,
+ self.repr, self.hash, self.compare,
+ self.metadata))
+
+# A sentinel object for default values to signal that a default
+# factory will be used. This is given a nice repr() which will appear
+# in the function signature of dataclasses' constructors.
+class _HAS_DEFAULT_FACTORY_CLASS:
+ def __repr__(self):
+ return ''
+_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS()
+
+def dataclass(*args, **kwds):
+ raise NotImplementedError("Standard library 'dataclasses' module"
+ "is unavailable, likely due to the version of Python you're using.")
+
+# Markers for the various kinds of fields and pseudo-fields.
+class _FIELD_BASE:
+ def __init__(self, name):
+ self.name = name
+ def __repr__(self):
+ return self.name
+_FIELD = _FIELD_BASE('_FIELD')
+_FIELD_CLASSVAR = _FIELD_BASE('_FIELD_CLASSVAR')
+_FIELD_INITVAR = _FIELD_BASE('_FIELD_INITVAR')
+
+def field(*ignore, **kwds):
+ default = kwds.pop("default", MISSING)
+ default_factory = kwds.pop("default_factory", MISSING)
+ init = kwds.pop("init", True)
+ repr = kwds.pop("repr", True)
+ hash = kwds.pop("hash", None)
+ compare = kwds.pop("compare", True)
+ metadata = kwds.pop("metadata", None)
+
+ if kwds:
+ raise ValueError("field received unexpected keyword arguments: %s"
+ % list(kwds.keys()))
+ if default is not MISSING and default_factory is not MISSING:
+ raise ValueError('cannot specify both default and default_factory')
+ if ignore:
+ raise ValueError("'field' does not take any positional arguments")
+ return Field(default, default_factory, init, repr, hash, compare, metadata)
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Embed.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Embed.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Embed.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Embed.c 2022-03-24 10:16:46.000000000 +0000
@@ -5,12 +5,13 @@
#endif
#if PY_MAJOR_VERSION < 3
-int %(main_method)s(int argc, char** argv) {
-#elif defined(WIN32) || defined(MS_WINDOWS)
-int %(wmain_method)s(int argc, wchar_t **argv) {
+int %(main_method)s(int argc, char** argv)
+#elif defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
+int %(wmain_method)s(int argc, wchar_t **argv)
#else
-static int __Pyx_main(int argc, wchar_t **argv) {
+static int __Pyx_main(int argc, wchar_t **argv)
#endif
+{
/* 754 requires that FP exceptions run in "no stop" mode by default,
* and until C vendors implement C99's ways to control FP exceptions,
* Python requires non-stop mode. Alas, some platforms enable FP
@@ -24,18 +25,23 @@
#endif
if (argc && argv)
Py_SetProgramName(argv[0]);
+
+ #if PY_MAJOR_VERSION < 3
+ if (PyImport_AppendInittab("%(module_name)s", init%(module_name)s) < 0) return 1;
+ #else
+ if (PyImport_AppendInittab("%(module_name)s", PyInit_%(module_name)s) < 0) return 1;
+ #endif
+
Py_Initialize();
if (argc && argv)
PySys_SetArgv(argc, argv);
+
{ /* init module '%(module_name)s' as '__main__' */
PyObject* m = NULL;
%(module_is_main)s = 1;
- #if PY_MAJOR_VERSION < 3
- init%(module_name)s();
- #else
- m = PyInit_%(module_name)s();
- #endif
- if (PyErr_Occurred()) {
+ m = PyImport_ImportModule("%(module_name)s");
+
+ if (!m && PyErr_Occurred()) {
PyErr_Print(); /* This exits with the right code if SystemExit. */
#if PY_MAJOR_VERSION < 3
if (Py_FlushLine()) PyErr_Clear();
@@ -44,14 +50,21 @@
}
Py_XDECREF(m);
}
+#if PY_VERSION_HEX < 0x03060000
Py_Finalize();
+#else
+ if (Py_FinalizeEx() < 0)
+ return 2;
+#endif
return 0;
}
-#if PY_MAJOR_VERSION >= 3 && !defined(WIN32) && !defined(MS_WINDOWS)
+#if PY_MAJOR_VERSION >= 3 && !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS)
#include
+#if PY_VERSION_HEX < 0x03050000
+
static wchar_t*
__Pyx_char2wchar(char* arg)
{
@@ -156,6 +169,8 @@
return NULL;
}
+#endif
+
int
%(main_method)s(int argc, char **argv)
{
@@ -178,7 +193,12 @@
res = 0;
setlocale(LC_ALL, "");
for (i = 0; i < argc; i++) {
- argv_copy2[i] = argv_copy[i] = __Pyx_char2wchar(argv[i]);
+ argv_copy2[i] = argv_copy[i] =
+#if PY_VERSION_HEX < 0x03050000
+ __Pyx_char2wchar(argv[i]);
+#else
+ Py_DecodeLocale(argv[i], NULL);
+#endif
if (!argv_copy[i]) res = 1; /* failure, but continue to simplify cleanup */
}
setlocale(LC_ALL, oldloc);
@@ -186,7 +206,11 @@
if (res == 0)
res = __Pyx_main(argc, argv_copy);
for (i = 0; i < argc; i++) {
+#if PY_VERSION_HEX < 0x03050000
free(argv_copy2[i]);
+#else
+ PyMem_RawFree(argv_copy2[i]);
+#endif
}
free(argv_copy);
free(argv_copy2);
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Exceptions.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Exceptions.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Exceptions.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Exceptions.c 2022-03-24 10:16:46.000000000 +0000
@@ -6,15 +6,34 @@
// __Pyx_GetException()
+/////////////// ErrOccurredWithGIL.proto ///////////////
+static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
+
+/////////////// ErrOccurredWithGIL ///////////////
+static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
+ int err;
+ #ifdef WITH_THREAD
+ PyGILState_STATE _save = PyGILState_Ensure();
+ #endif
+ err = !!PyErr_Occurred();
+ #ifdef WITH_THREAD
+ PyGILState_Release(_save);
+ #endif
+ return err;
+}
+
+
/////////////// PyThreadStateGet.proto ///////////////
//@substitute: naming
#if CYTHON_FAST_THREAD_STATE
#define __Pyx_PyThreadState_declare PyThreadState *$local_tstate_cname;
-#define __Pyx_PyThreadState_assign $local_tstate_cname = PyThreadState_GET();
+#define __Pyx_PyThreadState_assign $local_tstate_cname = __Pyx_PyThreadState_Current;
+#define __Pyx_PyErr_Occurred() $local_tstate_cname->curexc_type
#else
#define __Pyx_PyThreadState_declare
#define __Pyx_PyThreadState_assign
+#define __Pyx_PyErr_Occurred() PyErr_Occurred()
#endif
@@ -31,11 +50,28 @@
/////////////// PyErrExceptionMatches ///////////////
#if CYTHON_FAST_THREAD_STATE
+static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(tuple);
+#if PY_MAJOR_VERSION >= 3
+ // the tighter subtype checking in Py3 allows faster out-of-order comparison
+ for (i=0; icurexc_type;
if (exc_type == err) return 1;
if (unlikely(!exc_type)) return 0;
- return PyErr_GivenExceptionMatches(exc_type, err);
+ if (unlikely(PyTuple_Check(err)))
+ return __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err);
+ return __Pyx_PyErr_GivenExceptionMatches(exc_type, err);
}
#endif
@@ -44,6 +80,7 @@
//@requires: PyThreadStateGet
#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL)
#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb)
#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb)
#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState($local_tstate_cname, type, value, tb)
@@ -51,9 +88,19 @@
static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); /*proto*/
static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); /*proto*/
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL))
+#else
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
+#endif
+
#else
+#define __Pyx_PyErr_Clear() PyErr_Clear()
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb)
#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb)
#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb)
#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb)
#endif
@@ -97,9 +144,9 @@
// has changed quite a lot between the two versions.
#if PY_MAJOR_VERSION < 3
-static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb,
- CYTHON_UNUSED PyObject *cause) {
+static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) {
__Pyx_PyThreadState_declare
+ CYTHON_UNUSED_VAR(cause);
/* 'cause' is only used in Py3 */
Py_XINCREF(type);
if (!value || value == Py_None)
@@ -230,11 +277,7 @@
goto bad;
}
-#if PY_VERSION_HEX >= 0x03030000
if (cause) {
-#else
- if (cause && cause != Py_None) {
-#endif
PyObject *fixed_cause;
if (cause == Py_None) {
// raise ... from None
@@ -258,14 +301,14 @@
PyErr_SetObject(type, value);
if (tb) {
-#if CYTHON_COMPILING_IN_PYPY
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API
PyObject *tmp_type, *tmp_value, *tmp_tb;
PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb);
Py_INCREF(tb);
PyErr_Restore(tmp_type, tmp_value, tb);
Py_XDECREF(tmp_tb);
#else
- PyThreadState *tstate = PyThreadState_GET();
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
PyObject* tmp_tb = tstate->curexc_traceback;
if (tb != tmp_tb) {
Py_INCREF(tb);
@@ -281,6 +324,31 @@
}
#endif
+
+/////////////// GetTopmostException.proto ///////////////
+
+#if CYTHON_USE_EXC_INFO_STACK
+static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate);
+#endif
+
+/////////////// GetTopmostException ///////////////
+
+#if CYTHON_USE_EXC_INFO_STACK
+// Copied from errors.c in CPython.
+static _PyErr_StackItem *
+__Pyx_PyErr_GetTopmostException(PyThreadState *tstate)
+{
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
+ exc_info->previous_item != NULL)
+ {
+ exc_info = exc_info->previous_item;
+ }
+ return exc_info;
+}
+#endif
+
+
/////////////// GetException.proto ///////////////
//@substitute: naming
//@requires: PyThreadStateGet
@@ -295,10 +363,11 @@
/////////////// GetException ///////////////
#if CYTHON_FAST_THREAD_STATE
-static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb)
#else
-static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) {
+static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb)
#endif
+{
PyObject *local_type, *local_value, *local_tb;
#if CYTHON_FAST_THREAD_STATE
PyObject *tmp_type, *tmp_value, *tmp_tb;
@@ -333,12 +402,33 @@
*value = local_value;
*tb = local_tb;
#if CYTHON_FAST_THREAD_STATE
+ #if CYTHON_USE_EXC_INFO_STACK
+ {
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = local_value;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_tb);
+ #else
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = local_type;
+ exc_info->exc_value = local_value;
+ exc_info->exc_traceback = local_tb;
+ #endif
+ }
+ #else
tmp_type = tstate->exc_type;
tmp_value = tstate->exc_value;
tmp_tb = tstate->exc_traceback;
tstate->exc_type = local_type;
tstate->exc_value = local_value;
tstate->exc_traceback = local_tb;
+ #endif
// Make sure tstate is in a consistent state when we XDECREF
// these objects (DECREF may run arbitrary code).
Py_XDECREF(tmp_type);
@@ -362,34 +452,51 @@
static CYTHON_INLINE void __Pyx_ReraiseException(void); /*proto*/
-/////////////// ReRaiseException.proto ///////////////
+/////////////// ReRaiseException ///////////////
+//@requires: GetTopmostException
static CYTHON_INLINE void __Pyx_ReraiseException(void) {
PyObject *type = NULL, *value = NULL, *tb = NULL;
#if CYTHON_FAST_THREAD_STATE
PyThreadState *tstate = PyThreadState_GET();
+ #if CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ value = exc_info->exc_value;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ if (unlikely(value == Py_None)) {
+ value = NULL;
+ } else if (value) {
+ Py_INCREF(value);
+ type = (PyObject*) Py_TYPE(value);
+ Py_INCREF(type);
+ tb = PyException_GetTraceback(value);
+ }
+ #else
+ type = exc_info->exc_type;
+ tb = exc_info->exc_traceback;
+ Py_XINCREF(type);
+ Py_XINCREF(value);
+ Py_XINCREF(tb);
+ #endif
+ #else
type = tstate->exc_type;
value = tstate->exc_value;
tb = tstate->exc_traceback;
+ Py_XINCREF(type);
+ Py_XINCREF(value);
+ Py_XINCREF(tb);
+ #endif
#else
PyErr_GetExcInfo(&type, &value, &tb);
#endif
- if (!type || type == Py_None) {
-#if !CYTHON_FAST_THREAD_STATE
+ if (unlikely(!type || type == Py_None)) {
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(tb);
-#endif
// message copied from Py3
PyErr_SetString(PyExc_RuntimeError,
"No active exception to reraise");
} else {
-#if CYTHON_FAST_THREAD_STATE
- Py_INCREF(type);
- Py_XINCREF(value);
- Py_XINCREF(tb);
-
-#endif
PyErr_Restore(type, value, tb);
}
}
@@ -411,28 +518,73 @@
#endif
/////////////// SaveResetException ///////////////
+//@requires: GetTopmostException
#if CYTHON_FAST_THREAD_STATE
static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ PyObject *exc_value = exc_info->exc_value;
+ if (exc_value == NULL || exc_value == Py_None) {
+ *value = NULL;
+ *type = NULL;
+ *tb = NULL;
+ } else {
+ *value = exc_value;
+ Py_INCREF(*value);
+ *type = (PyObject*) Py_TYPE(exc_value);
+ Py_INCREF(*type);
+ *tb = PyException_GetTraceback(exc_value);
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ *type = exc_info->exc_type;
+ *value = exc_info->exc_value;
+ *tb = exc_info->exc_traceback;
+ Py_XINCREF(*type);
+ Py_XINCREF(*value);
+ Py_XINCREF(*tb);
+ #else
*type = tstate->exc_type;
*value = tstate->exc_value;
*tb = tstate->exc_traceback;
Py_XINCREF(*type);
Py_XINCREF(*value);
Py_XINCREF(*tb);
+ #endif
}
static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ PyObject *tmp_value = exc_info->exc_value;
+ exc_info->exc_value = value;
+ Py_XDECREF(tmp_value);
+ // TODO: avoid passing these at all
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+ #else
PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = type;
+ exc_info->exc_value = value;
+ exc_info->exc_traceback = tb;
+ #else
tmp_type = tstate->exc_type;
tmp_value = tstate->exc_value;
tmp_tb = tstate->exc_traceback;
tstate->exc_type = type;
tstate->exc_value = value;
tstate->exc_traceback = tb;
+ #endif
Py_XDECREF(tmp_type);
Py_XDECREF(tmp_value);
Py_XDECREF(tmp_tb);
+ #endif
}
#endif
@@ -452,6 +604,31 @@
#if CYTHON_FAST_THREAD_STATE
static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = *value;
+ if (tmp_value == NULL || tmp_value == Py_None) {
+ Py_XDECREF(tmp_value);
+ tmp_value = NULL;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ } else {
+ // TODO: avoid swapping these at all
+ tmp_type = (PyObject*) Py_TYPE(tmp_value);
+ Py_INCREF(tmp_type);
+ tmp_tb = PyException_GetTraceback(tmp_value);
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+
+ exc_info->exc_type = *type;
+ exc_info->exc_value = *value;
+ exc_info->exc_traceback = *tb;
+ #else
tmp_type = tstate->exc_type;
tmp_value = tstate->exc_value;
tmp_tb = tstate->exc_traceback;
@@ -459,6 +636,7 @@
tstate->exc_type = *type;
tstate->exc_value = *value;
tstate->exc_traceback = *tb;
+ #endif
*type = tmp_type;
*value = tmp_value;
@@ -487,9 +665,9 @@
//@requires: PyErrFetchRestore
//@requires: PyThreadStateGet
-static void __Pyx_WriteUnraisable(const char *name, CYTHON_UNUSED int clineno,
- CYTHON_UNUSED int lineno, CYTHON_UNUSED const char *filename,
- int full_traceback, CYTHON_UNUSED int nogil) {
+static void __Pyx_WriteUnraisable(const char *name, int clineno,
+ int lineno, const char *filename,
+ int full_traceback, int nogil) {
PyObject *old_exc, *old_val, *old_tb;
PyObject *ctx;
__Pyx_PyThreadState_declare
@@ -502,6 +680,11 @@
else state = (PyGILState_STATE)-1;
#endif
#endif
+ CYTHON_UNUSED_VAR(clineno);
+ CYTHON_UNUSED_VAR(lineno);
+ CYTHON_UNUSED_VAR(filename);
+ CYTHON_MAYBE_UNUSED_VAR(nogil);
+
__Pyx_PyThreadState_assign
__Pyx_ErrFetch(&old_exc, &old_val, &old_tb);
if (full_traceback) {
@@ -529,6 +712,66 @@
#endif
}
+/////////////// CLineInTraceback.proto ///////////////
+
+#ifdef CYTHON_CLINE_IN_TRACEBACK /* 0 or 1 to disable/enable C line display in tracebacks at C compile time */
+#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0)
+#else
+static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);/*proto*/
+#endif
+
+/////////////// CLineInTraceback ///////////////
+//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError
+//@requires: ObjectHandling.c::PyDictVersioning
+//@requires: PyErrFetchRestore
+//@substitute: naming
+
+#ifndef CYTHON_CLINE_IN_TRACEBACK
+static int __Pyx_CLineForTraceback(CYTHON_NCP_UNUSED PyThreadState *tstate, int c_line) {
+ PyObject *use_cline;
+ PyObject *ptype, *pvalue, *ptraceback;
+#if CYTHON_COMPILING_IN_CPYTHON
+ PyObject **cython_runtime_dict;
+#endif
+
+ if (unlikely(!${cython_runtime_cname})) {
+ // Very early error where the runtime module is not set up yet.
+ return c_line;
+ }
+
+ __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
+
+#if CYTHON_COMPILING_IN_CPYTHON
+ cython_runtime_dict = _PyObject_GetDictPtr(${cython_runtime_cname});
+ if (likely(cython_runtime_dict)) {
+ __PYX_PY_DICT_LOOKUP_IF_MODIFIED(
+ use_cline, *cython_runtime_dict,
+ __Pyx_PyDict_GetItemStr(*cython_runtime_dict, PYIDENT("cline_in_traceback")))
+ } else
+#endif
+ {
+ PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(${cython_runtime_cname}, PYIDENT("cline_in_traceback"));
+ if (use_cline_obj) {
+ use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True;
+ Py_DECREF(use_cline_obj);
+ } else {
+ PyErr_Clear();
+ use_cline = NULL;
+ }
+ }
+ if (!use_cline) {
+ c_line = 0;
+ // No need to handle errors here when we reset the exception state just afterwards.
+ (void) PyObject_SetAttr(${cython_runtime_cname}, PYIDENT("cline_in_traceback"), Py_False);
+ }
+ else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) {
+ c_line = 0;
+ }
+ __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
+ return c_line;
+}
+#endif
+
/////////////// AddTraceback.proto ///////////////
static void __Pyx_AddTraceback(const char *funcname, int c_line,
@@ -536,42 +779,63 @@
/////////////// AddTraceback ///////////////
//@requires: ModuleSetupCode.c::CodeObjectCache
+//@requires: CLineInTraceback
//@substitute: naming
#include "compile.h"
#include "frameobject.h"
#include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ if (c_line) {
+ // Avoid "unused" warning as long as we don't use this.
+ (void) $cfilenm_cname;
+ (void) __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line);
+ }
+ _PyTraceback_Add(funcname, filename, py_line);
+}
+#else
static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
const char *funcname, int c_line,
int py_line, const char *filename) {
- PyCodeObject *py_code = 0;
- PyObject *py_srcfile = 0;
- PyObject *py_funcname = 0;
-
+ PyCodeObject *py_code = NULL;
+ PyObject *py_funcname = NULL;
#if PY_MAJOR_VERSION < 3
+ PyObject *py_srcfile = NULL;
+
py_srcfile = PyString_FromString(filename);
- #else
- py_srcfile = PyUnicode_FromString(filename);
- #endif
if (!py_srcfile) goto bad;
+ #endif
+
if (c_line) {
#if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, $cfilenm_cname, c_line);
+ if (!py_funcname) goto bad;
#else
py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, $cfilenm_cname, c_line);
+ if (!py_funcname) goto bad;
+ funcname = PyUnicode_AsUTF8(py_funcname);
+ if (!funcname) goto bad;
#endif
}
else {
#if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromString(funcname);
- #else
- py_funcname = PyUnicode_FromString(funcname);
+ if (!py_funcname) goto bad;
#endif
}
- if (!py_funcname) goto bad;
+ #if PY_MAJOR_VERSION < 3
py_code = __Pyx_PyCode_New(
0, /*int argcount,*/
+ 0, /*int posonlyargcount,*/
0, /*int kwonlyargcount,*/
0, /*int nlocals,*/
0, /*int stacksize,*/
@@ -588,11 +852,16 @@
$empty_bytes /*PyObject *lnotab*/
);
Py_DECREF(py_srcfile);
- Py_DECREF(py_funcname);
+ #else
+ py_code = PyCode_NewEmpty(filename, funcname, py_line);
+ #endif
+ Py_XDECREF(py_funcname); // XDECREF since it's only set on Py3 if cline
return py_code;
bad:
- Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname);
+ #if PY_MAJOR_VERSION < 3
+ Py_XDECREF(py_srcfile);
+ #endif
return NULL;
}
@@ -600,19 +869,25 @@
int py_line, const char *filename) {
PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0;
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
+
+ if (c_line) {
+ c_line = __Pyx_CLineForTraceback(tstate, c_line);
+ }
- py_code = $global_code_object_cache_find(c_line ? c_line : py_line);
+ // Negate to avoid collisions between py and c lines.
+ py_code = $global_code_object_cache_find(c_line ? -c_line : py_line);
if (!py_code) {
py_code = __Pyx_CreateCodeObjectForTraceback(
funcname, c_line, py_line, filename);
if (!py_code) goto bad;
- $global_code_object_cache_insert(c_line ? c_line : py_line, py_code);
+ $global_code_object_cache_insert(c_line ? -c_line : py_line, py_code);
}
py_frame = PyFrame_New(
- PyThreadState_GET(), /*PyThreadState *tstate,*/
- py_code, /*PyCodeObject *code,*/
- $moddict_cname, /*PyObject *globals,*/
- 0 /*PyObject *locals*/
+ tstate, /*PyThreadState *tstate,*/
+ py_code, /*PyCodeObject *code,*/
+ $moddict_cname, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
);
if (!py_frame) goto bad;
__Pyx_PyFrame_SetLineNumber(py_frame, py_line);
@@ -621,3 +896,4 @@
Py_XDECREF(py_code);
Py_XDECREF(py_frame);
}
+#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/ExtensionTypes.c cython-0.20.1+1~202203241016-9537/Cython/Utility/ExtensionTypes.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/ExtensionTypes.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/ExtensionTypes.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,3 +1,322 @@
+/////////////// FixUpExtensionType.proto ///////////////
+
+#if CYTHON_USE_TYPE_SPECS
+static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type); /*proto*/
+#endif
+
+/////////////// FixUpExtensionType ///////////////
+//@requires:ModuleSetupCode.c::IncludeStructmemberH
+//@requires:StringTools.c::IncludeStringH
+
+#if CYTHON_USE_TYPE_SPECS
+static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) {
+#if PY_VERSION_HEX > 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ (void) spec;
+ (void) type;
+#else
+ // Set tp_weakreflist, tp_dictoffset, tp_vectorcalloffset
+ // Copied and adapted from https://bugs.python.org/issue38140
+ const PyType_Slot *slot = spec->slots;
+ while (slot && slot->slot && slot->slot != Py_tp_members)
+ slot++;
+ if (slot && slot->slot == Py_tp_members) {
+ int changed = 0;
+#if !(PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON)
+ const
+#endif
+ PyMemberDef *memb = (PyMemberDef*) slot->pfunc;
+ while (memb && memb->name) {
+ if (memb->name[0] == '_' && memb->name[1] == '_') {
+#if PY_VERSION_HEX < 0x030900b1
+ if (strcmp(memb->name, "__weaklistoffset__") == 0) {
+ // The PyMemberDef must be a Py_ssize_t and readonly.
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_weaklistoffset = memb->offset;
+ // FIXME: is it even worth calling PyType_Modified() here?
+ changed = 1;
+ }
+ else if (strcmp(memb->name, "__dictoffset__") == 0) {
+ // The PyMemberDef must be a Py_ssize_t and readonly.
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_dictoffset = memb->offset;
+ // FIXME: is it even worth calling PyType_Modified() here?
+ changed = 1;
+ }
+#if CYTHON_METH_FASTCALL
+ else if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
+ // The PyMemberDef must be a Py_ssize_t and readonly.
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+#if PY_VERSION_HEX >= 0x030800b4
+ type->tp_vectorcall_offset = memb->offset;
+#else
+ type->tp_print = (printfunc) memb->offset;
+#endif
+ // FIXME: is it even worth calling PyType_Modified() here?
+ changed = 1;
+ }
+#endif
+#else
+ if ((0));
+#endif
+#if PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON
+ else if (strcmp(memb->name, "__module__") == 0) {
+ // PyType_FromSpec() in CPython <= 3.9b1 overwrites this field with a constant string.
+ // See https://bugs.python.org/issue40703
+ PyObject *descr;
+ // The PyMemberDef must be an object and normally readable, possibly writable.
+ assert(memb->type == T_OBJECT);
+ assert(memb->flags == 0 || memb->flags == READONLY);
+ descr = PyDescr_NewMember(type, memb);
+ if (unlikely(!descr))
+ return -1;
+ if (unlikely(PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr) < 0)) {
+ Py_DECREF(descr);
+ return -1;
+ }
+ Py_DECREF(descr);
+ changed = 1;
+ }
+#endif
+ }
+ memb++;
+ }
+ if (changed)
+ PyType_Modified(type);
+ }
+#endif
+ return 0;
+}
+#endif
+
+
+/////////////// ValidateBasesTuple.proto ///////////////
+
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases); /*proto*/
+#endif
+
+/////////////// ValidateBasesTuple ///////////////
+
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) {
+ // Loop over all bases (except the first) and check that those
+ // really are heap types. Otherwise, it would not be safe to
+ // subclass them.
+ //
+ // We also check tp_dictoffset: it is unsafe to inherit
+ // tp_dictoffset from a base class because the object structures
+ // would not be compatible. So, if our extension type doesn't set
+ // tp_dictoffset (i.e. there is no __dict__ attribute in the object
+ // structure), we need to check that none of the base classes sets
+ // it either.
+ Py_ssize_t i, n = PyTuple_GET_SIZE(bases);
+ for (i = 1; i < n; i++) /* Skip first base */
+ {
+ PyObject *b0 = PyTuple_GET_ITEM(bases, i);
+ PyTypeObject *b;
+#if PY_MAJOR_VERSION < 3
+ /* Disallow old-style classes */
+ if (PyClass_Check(b0))
+ {
+ PyErr_Format(PyExc_TypeError, "base class '%.200s' is an old-style class",
+ PyString_AS_STRING(((PyClassObject*)b0)->cl_name));
+ return -1;
+ }
+#endif
+ b = (PyTypeObject*) b0;
+ if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE))
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetName(b);
+ PyErr_Format(PyExc_TypeError,
+ "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name);
+ __Pyx_DECREF_TypeName(b_name);
+ return -1;
+ }
+ if (dictoffset == 0 && b->tp_dictoffset)
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetName(b);
+ PyErr_Format(PyExc_TypeError,
+ "extension type '%.200s' has no __dict__ slot, "
+ "but base type '" __Pyx_FMT_TYPENAME "' has: "
+ "either add 'cdef dict __dict__' to the extension type "
+ "or add '__slots__ = [...]' to the base type",
+ type_name, b_name);
+ __Pyx_DECREF_TypeName(b_name);
+ return -1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+/////////////// PyType_Ready.proto ///////////////
+
+// unused when using type specs
+static CYTHON_UNUSED int __Pyx_PyType_Ready(PyTypeObject *t);/*proto*/
+
+/////////////// PyType_Ready ///////////////
+//@requires: ObjectHandling.c::PyObjectCallMethod0
+//@requires: ValidateBasesTuple
+
+// Wrapper around PyType_Ready() with some runtime checks and fixes
+// to deal with multiple inheritance.
+static int __Pyx_PyType_Ready(PyTypeObject *t) {
+
+// FIXME: is this really suitable for CYTHON_COMPILING_IN_LIMITED_API?
+#if CYTHON_USE_TYPE_SPECS || !(CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API) || defined(PYSTON_MAJOR_VERSION)
+ // avoid C warning about unused helper function
+ (void)__Pyx_PyObject_CallMethod0;
+#if CYTHON_USE_TYPE_SPECS
+ (void)__Pyx_validate_bases_tuple;
+#endif
+
+ return PyType_Ready(t);
+
+#else
+ int r;
+ PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*);
+ if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1))
+ return -1;
+
+#if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION)
+ {
+ // Make sure GC does not pick up our non-heap type as heap type with this hack!
+ // For details, see https://github.com/cython/cython/issues/3603
+ int gc_was_enabled;
+ #if PY_VERSION_HEX >= 0x030A00b1
+ // finally added in Py3.10 :)
+ gc_was_enabled = PyGC_Disable();
+ (void)__Pyx_PyObject_CallMethod0;
+
+ #else
+ // Call gc.disable() as a backwards compatible fallback, but only if needed.
+ PyObject *ret, *py_status;
+ PyObject *gc = NULL;
+ #if PY_VERSION_HEX >= 0x030700a1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400)
+ // https://foss.heptapod.net/pypy/pypy/-/issues/3385
+ gc = PyImport_GetModule(PYUNICODE("gc"));
+ #endif
+ if (unlikely(!gc)) gc = PyImport_Import(PYUNICODE("gc"));
+ if (unlikely(!gc)) return -1;
+ py_status = __Pyx_PyObject_CallMethod0(gc, PYUNICODE("isenabled"));
+ if (unlikely(!py_status)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ gc_was_enabled = __Pyx_PyObject_IsTrue(py_status);
+ Py_DECREF(py_status);
+ if (gc_was_enabled > 0) {
+ ret = __Pyx_PyObject_CallMethod0(gc, PYUNICODE("disable"));
+ if (unlikely(!ret)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ Py_DECREF(ret);
+ } else if (unlikely(gc_was_enabled == -1)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ #endif
+
+ // As of https://bugs.python.org/issue22079
+ // PyType_Ready enforces that all bases of a non-heap type are
+ // non-heap. We know that this is the case for the solid base but
+ // other bases are heap allocated and are kept alive through the
+ // tp_bases reference.
+ // Other than this check, the Py_TPFLAGS_HEAPTYPE flag is unused
+ // in PyType_Ready().
+ t->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+#else
+ // avoid C warning about unused helper function
+ (void)__Pyx_PyObject_CallMethod0;
+#endif
+
+ r = PyType_Ready(t);
+
+#if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION)
+ t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE;
+
+ #if PY_VERSION_HEX >= 0x030A00b1
+ if (gc_was_enabled)
+ PyGC_Enable();
+ #else
+ if (gc_was_enabled) {
+ PyObject *tp, *v, *tb;
+ PyErr_Fetch(&tp, &v, &tb);
+ ret = __Pyx_PyObject_CallMethod0(gc, PYUNICODE("enable"));
+ if (likely(ret || r == -1)) {
+ Py_XDECREF(ret);
+ // do not overwrite exceptions raised by PyType_Ready() above
+ PyErr_Restore(tp, v, tb);
+ } else {
+ // PyType_Ready() succeeded, but gc.enable() failed.
+ Py_XDECREF(tp);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+ r = -1;
+ }
+ }
+ Py_DECREF(gc);
+ #endif
+ }
+#endif
+
+ return r;
+#endif
+}
+
+
+/////////////// PyTrashcan.proto ///////////////
+
+// These macros are taken from https://github.com/python/cpython/pull/11841
+// Unlike the Py_TRASHCAN_SAFE_BEGIN/Py_TRASHCAN_SAFE_END macros, they
+// allow dealing correctly with subclasses.
+
+// This requires CPython version >= 2.7.4
+// (or >= 3.2.4 but we don't support such old Python 3 versions anyway)
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03080000
+// https://github.com/python/cpython/pull/11841 merged so Cython reimplementation
+// is no longer necessary
+#define __Pyx_TRASHCAN_BEGIN Py_TRASHCAN_BEGIN
+#define __Pyx_TRASHCAN_END Py_TRASHCAN_END
+#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070400
+#define __Pyx_TRASHCAN_BEGIN_CONDITION(op, cond) \
+ do { \
+ PyThreadState *_tstate = NULL; \
+ // If "cond" is false, then _tstate remains NULL and the deallocator
+ // is run normally without involving the trashcan
+ if (cond) { \
+ _tstate = PyThreadState_GET(); \
+ if (_tstate->trash_delete_nesting >= PyTrash_UNWIND_LEVEL) { \
+ // Store the object (to be deallocated later) and jump past
+ // Py_TRASHCAN_END, skipping the body of the deallocator
+ _PyTrash_thread_deposit_object((PyObject*)(op)); \
+ break; \
+ } \
+ ++_tstate->trash_delete_nesting; \
+ }
+ // The body of the deallocator is here.
+#define __Pyx_TRASHCAN_END \
+ if (_tstate) { \
+ --_tstate->trash_delete_nesting; \
+ if (_tstate->trash_delete_later && _tstate->trash_delete_nesting <= 0) \
+ _PyTrash_thread_destroy_chain(); \
+ } \
+ } while (0);
+
+#define __Pyx_TRASHCAN_BEGIN(op, dealloc) __Pyx_TRASHCAN_BEGIN_CONDITION(op, \
+ __Pyx_PyObject_GetSlot(op, tp_dealloc, destructor) == (destructor)(dealloc))
+
+#else
+// The trashcan is a no-op on other Python implementations
+// or old CPython versions
+#define __Pyx_TRASHCAN_BEGIN(op, dealloc)
+#define __Pyx_TRASHCAN_END
+#endif
/////////////// CallNextTpDealloc.proto ///////////////
@@ -7,13 +326,14 @@
static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_dealloc) {
PyTypeObject* type = Py_TYPE(obj);
+ destructor tp_dealloc = NULL;
/* try to find the first parent type that has a different tp_dealloc() function */
- while (type && type->tp_dealloc != current_tp_dealloc)
- type = type->tp_base;
- while (type && type->tp_dealloc == current_tp_dealloc)
- type = type->tp_base;
+ while (type && __Pyx_PyType_GetSlot(type, tp_dealloc, destructor) != current_tp_dealloc)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
+ while (type && (tp_dealloc = __Pyx_PyType_GetSlot(type, tp_dealloc, destructor)) == current_tp_dealloc)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
if (type)
- type->tp_dealloc(obj);
+ tp_dealloc(obj);
}
/////////////// CallNextTpTraverse.proto ///////////////
@@ -24,30 +344,202 @@
static int __Pyx_call_next_tp_traverse(PyObject* obj, visitproc v, void *a, traverseproc current_tp_traverse) {
PyTypeObject* type = Py_TYPE(obj);
+ traverseproc tp_traverse = NULL;
/* try to find the first parent type that has a different tp_traverse() function */
- while (type && type->tp_traverse != current_tp_traverse)
- type = type->tp_base;
- while (type && type->tp_traverse == current_tp_traverse)
- type = type->tp_base;
- if (type && type->tp_traverse)
- return type->tp_traverse(obj, v, a);
+ while (type && __Pyx_PyType_GetSlot(type, tp_traverse, traverseproc) != current_tp_traverse)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
+ while (type && (tp_traverse = __Pyx_PyType_GetSlot(type, tp_traverse, traverseproc)) == current_tp_traverse)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
+ if (type && tp_traverse)
+ return tp_traverse(obj, v, a);
// FIXME: really ignore?
return 0;
}
/////////////// CallNextTpClear.proto ///////////////
-static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_dealloc);
+static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear);
/////////////// CallNextTpClear ///////////////
static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear) {
PyTypeObject* type = Py_TYPE(obj);
+ inquiry tp_clear = NULL;
/* try to find the first parent type that has a different tp_clear() function */
- while (type && type->tp_clear != current_tp_clear)
- type = type->tp_base;
- while (type && type->tp_clear == current_tp_clear)
- type = type->tp_base;
- if (type && type->tp_clear)
- type->tp_clear(obj);
+ while (type && __Pyx_PyType_GetSlot(type, tp_clear, inquiry) != current_tp_clear)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
+ while (type && (tp_clear = __Pyx_PyType_GetSlot(type, tp_clear, inquiry)) == current_tp_clear)
+ type = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
+ if (type && tp_clear)
+ tp_clear(obj);
+}
+
+/////////////// SetupReduce.proto ///////////////
+
+#if !CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_setup_reduce(PyObject* type_obj);
+#endif
+
+/////////////// SetupReduce ///////////////
+//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError
+//@requires: ObjectHandling.c::PyObjectGetAttrStr
+//@substitute: naming
+
+#if !CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) {
+ int ret;
+ PyObject *name_attr;
+
+ name_attr = __Pyx_PyObject_GetAttrStrNoError(meth, PYIDENT("__name__"));
+ if (likely(name_attr)) {
+ ret = PyObject_RichCompareBool(name_attr, name, Py_EQ);
+ } else {
+ ret = -1;
+ }
+
+ if (unlikely(ret < 0)) {
+ PyErr_Clear();
+ ret = 0;
+ }
+
+ Py_XDECREF(name_attr);
+ return ret;
+}
+
+static int __Pyx_setup_reduce(PyObject* type_obj) {
+ int ret = 0;
+ PyObject *object_reduce = NULL;
+ PyObject *object_reduce_ex = NULL;
+ PyObject *reduce = NULL;
+ PyObject *reduce_ex = NULL;
+ PyObject *reduce_cython = NULL;
+ PyObject *setstate = NULL;
+ PyObject *setstate_cython = NULL;
+
+#if CYTHON_USE_PYTYPE_LOOKUP
+ if (_PyType_Lookup((PyTypeObject*)type_obj, PYIDENT("__getstate__"))) goto __PYX_GOOD;
+#else
+ if (PyObject_HasAttr(type_obj, PYIDENT("__getstate__"))) goto __PYX_GOOD;
+#endif
+
+#if CYTHON_USE_PYTYPE_LOOKUP
+ object_reduce_ex = _PyType_Lookup(&PyBaseObject_Type, PYIDENT("__reduce_ex__")); if (!object_reduce_ex) goto __PYX_BAD;
+#else
+ object_reduce_ex = __Pyx_PyObject_GetAttrStr((PyObject*)&PyBaseObject_Type, PYIDENT("__reduce_ex__")); if (!object_reduce_ex) goto __PYX_BAD;
+#endif
+
+ reduce_ex = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__reduce_ex__")); if (unlikely(!reduce_ex)) goto __PYX_BAD;
+ if (reduce_ex == object_reduce_ex) {
+
+#if CYTHON_USE_PYTYPE_LOOKUP
+ object_reduce = _PyType_Lookup(&PyBaseObject_Type, PYIDENT("__reduce__")); if (!object_reduce) goto __PYX_BAD;
+#else
+ object_reduce = __Pyx_PyObject_GetAttrStr((PyObject*)&PyBaseObject_Type, PYIDENT("__reduce__")); if (!object_reduce) goto __PYX_BAD;
+#endif
+ reduce = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__reduce__")); if (unlikely(!reduce)) goto __PYX_BAD;
+
+ if (reduce == object_reduce || __Pyx_setup_reduce_is_named(reduce, PYIDENT("__reduce_cython__"))) {
+ reduce_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__reduce_cython__"));
+ if (likely(reduce_cython)) {
+ ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
+ ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
+ } else if (reduce == object_reduce || PyErr_Occurred()) {
+ // Ignore if we're done, i.e. if 'reduce' already has the right name and the original is gone.
+ // Otherwise: error.
+ goto __PYX_BAD;
+ }
+
+ setstate = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__setstate__"));
+ if (!setstate) PyErr_Clear();
+ if (!setstate || __Pyx_setup_reduce_is_named(setstate, PYIDENT("__setstate_cython__"))) {
+ setstate_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__setstate_cython__"));
+ if (likely(setstate_cython)) {
+ ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
+ ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
+ } else if (!setstate || PyErr_Occurred()) {
+ // Ignore if we're done, i.e. if 'setstate' already has the right name and the original is gone.
+ // Otherwise: error.
+ goto __PYX_BAD;
+ }
+ }
+ PyType_Modified((PyTypeObject*)type_obj);
+ }
+ }
+ goto __PYX_GOOD;
+
+__PYX_BAD:
+ if (!PyErr_Occurred()) {
+ __Pyx_TypeName type_obj_name =
+ __Pyx_PyType_GetName((PyTypeObject*)type_obj);
+ PyErr_Format(PyExc_RuntimeError,
+ "Unable to initialize pickling for " __Pyx_FMT_TYPENAME, type_obj_name);
+ __Pyx_DECREF_TypeName(type_obj_name);
+ }
+ ret = -1;
+__PYX_GOOD:
+#if !CYTHON_USE_PYTYPE_LOOKUP
+ Py_XDECREF(object_reduce);
+ Py_XDECREF(object_reduce_ex);
+#endif
+ Py_XDECREF(reduce);
+ Py_XDECREF(reduce_ex);
+ Py_XDECREF(reduce_cython);
+ Py_XDECREF(setstate);
+ Py_XDECREF(setstate_cython);
+ return ret;
+}
+#endif
+
+
+/////////////// BinopSlot ///////////////
+
+static CYTHON_INLINE PyObject *{{func_name}}_maybe_call_slot(PyTypeObject* type, PyObject *left, PyObject *right {{extra_arg_decl}}) {
+ {{slot_type}} slot;
+#if CYTHON_USE_TYPE_SLOTS || PY_MAJOR_VERSION < 3 || CYTHON_COMPILING_IN_PYPY
+ slot = type->tp_as_number ? type->tp_as_number->{{slot_name}} : NULL;
+#else
+ slot = ({{slot_type}}) PyType_GetSlot(type, Py_{{slot_name}});
+#endif
+ return slot ? slot(left, right {{extra_arg}}) : __Pyx_NewRef(Py_NotImplemented);
+}
+
+static PyObject *{{func_name}}(PyObject *left, PyObject *right {{extra_arg_decl}}) {
+ int maybe_self_is_left, maybe_self_is_right = 0;
+ maybe_self_is_left = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+ || (Py_TYPE(left)->tp_as_number && Py_TYPE(left)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+ || __Pyx_TypeCheck(left, {{type_cname}});
+ // Optimize for the common case where the left operation is defined (and successful).
+ if (!({{overloads_left}})) {
+ maybe_self_is_right = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+ || (Py_TYPE(right)->tp_as_number && Py_TYPE(right)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+ || __Pyx_TypeCheck(right, {{type_cname}});
+ }
+ if (maybe_self_is_left) {
+ PyObject *res;
+ if (maybe_self_is_right && {{overloads_right}} && !({{overloads_left}})) {
+ res = {{call_right}};
+ if (res != Py_NotImplemented) return res;
+ Py_DECREF(res);
+ // Don't bother calling it again.
+ maybe_self_is_right = 0;
+ }
+ res = {{call_left}};
+ if (res != Py_NotImplemented) return res;
+ Py_DECREF(res);
+ }
+ if (({{overloads_left}})) {
+ maybe_self_is_right = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+ || (Py_TYPE(right)->tp_as_number && Py_TYPE(right)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+ || PyType_IsSubtype(Py_TYPE(right), {{type_cname}});
+ }
+ if (maybe_self_is_right) {
+ return {{call_right}};
+ }
+ return __Pyx_NewRef(Py_NotImplemented);
}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/FunctionArguments.c cython-0.20.1+1~202203241016-9537/Cython/Utility/FunctionArguments.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/FunctionArguments.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/FunctionArguments.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,34 +1,37 @@
//////////////////// ArgTypeTest.proto ////////////////////
-static CYTHON_INLINE int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,
- const char *name, int exact); /*proto*/
-//////////////////// ArgTypeTest ////////////////////
+#define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \
+ ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 : \
+ __Pyx__ArgTypeTest(obj, type, name, exact))
-static void __Pyx_RaiseArgumentTypeInvalid(const char* name, PyObject *obj, PyTypeObject *type) {
- PyErr_Format(PyExc_TypeError,
- "Argument '%.200s' has incorrect type (expected %.200s, got %.200s)",
- name, type->tp_name, Py_TYPE(obj)->tp_name);
-}
+static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact); /*proto*/
-static CYTHON_INLINE int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,
- const char *name, int exact)
+//////////////////// ArgTypeTest ////////////////////
+
+static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact)
{
+ __Pyx_TypeName type_name;
+ __Pyx_TypeName obj_type_name;
if (unlikely(!type)) {
PyErr_SetString(PyExc_SystemError, "Missing type object");
return 0;
}
- if (none_allowed && obj == Py_None) return 1;
else if (exact) {
- if (likely(Py_TYPE(obj) == type)) return 1;
#if PY_MAJOR_VERSION == 2
- else if ((type == &PyBaseString_Type) && likely(__Pyx_PyBaseString_CheckExact(obj))) return 1;
+ if ((type == &PyBaseString_Type) && likely(__Pyx_PyBaseString_CheckExact(obj))) return 1;
#endif
}
else {
- if (likely(PyObject_TypeCheck(obj, type))) return 1;
+ if (likely(__Pyx_TypeCheck(obj, type))) return 1;
}
- __Pyx_RaiseArgumentTypeInvalid(name, obj, type);
+ type_name = __Pyx_PyType_GetName(type);
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME
+ ", got " __Pyx_FMT_TYPENAME ")", name, type_name, obj_type_name);
+ __Pyx_DECREF_TypeName(type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
return 0;
}
@@ -72,14 +75,11 @@
//////////////////// RaiseKeywordRequired.proto ////////////////////
-static CYTHON_INLINE void __Pyx_RaiseKeywordRequired(const char* func_name, PyObject* kw_name); /*proto*/
+static void __Pyx_RaiseKeywordRequired(const char* func_name, PyObject* kw_name); /*proto*/
//////////////////// RaiseKeywordRequired ////////////////////
-static CYTHON_INLINE void __Pyx_RaiseKeywordRequired(
- const char* func_name,
- PyObject* kw_name)
-{
+static void __Pyx_RaiseKeywordRequired(const char* func_name, PyObject* kw_name) {
PyErr_Format(PyExc_TypeError,
#if PY_MAJOR_VERSION >= 3
"%s() needs keyword-only argument %U", func_name, kw_name);
@@ -117,22 +117,28 @@
//////////////////// RaiseMappingExpected ////////////////////
static void __Pyx_RaiseMappingExpectedError(PyObject* arg) {
- PyErr_Format(PyExc_TypeError, "'%.200s' object is not a mapping", Py_TYPE(arg)->tp_name);
+ __Pyx_TypeName arg_type_name = __Pyx_PyType_GetName(Py_TYPE(arg));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is not a mapping", arg_type_name);
+ __Pyx_DECREF_TypeName(arg_type_name);
}
//////////////////// KeywordStringCheck.proto ////////////////////
-static CYTHON_INLINE int __Pyx_CheckKeywordStrings(PyObject *kwdict, const char* function_name, int kw_allowed); /*proto*/
+static int __Pyx_CheckKeywordStrings(PyObject *kw, const char* function_name, int kw_allowed); /*proto*/
//////////////////// KeywordStringCheck ////////////////////
-// __Pyx_CheckKeywordStrings raises an error if non-string keywords
-// were passed to a function, or if any keywords were passed to a
-// function that does not accept them.
+// __Pyx_CheckKeywordStrings raises an error if non-string keywords
+// were passed to a function, or if any keywords were passed to a
+// function that does not accept them.
+//
+// The "kw" argument is either a dict (for METH_VARARGS) or a tuple
+// (for METH_FASTCALL).
-static CYTHON_INLINE int __Pyx_CheckKeywordStrings(
- PyObject *kwdict,
+static int __Pyx_CheckKeywordStrings(
+ PyObject *kw,
const char* function_name,
int kw_allowed)
{
@@ -140,18 +146,37 @@
Py_ssize_t pos = 0;
#if CYTHON_COMPILING_IN_PYPY
/* PyPy appears to check keywords at call time, not at unpacking time => not much to do here */
- if (!kw_allowed && PyDict_Next(kwdict, &pos, &key, 0))
+ if (!kw_allowed && PyDict_Next(kw, &pos, &key, 0))
goto invalid_keyword;
return 1;
#else
- while (PyDict_Next(kwdict, &pos, &key, 0)) {
+ if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kw))) {
+ if (unlikely(PyTuple_GET_SIZE(kw) == 0))
+ return 1;
+ if (!kw_allowed) {
+ key = PyTuple_GET_ITEM(kw, 0);
+ goto invalid_keyword;
+ }
+#if PY_VERSION_HEX < 0x03090000
+ // On CPython >= 3.9, the FASTCALL protocol guarantees that keyword
+ // names are strings (see https://bugs.python.org/issue37540)
+ for (pos = 0; pos < PyTuple_GET_SIZE(kw); pos++) {
+ key = PyTuple_GET_ITEM(kw, pos);
+ if (unlikely(!PyUnicode_Check(key)))
+ goto invalid_keyword_type;
+ }
+#endif
+ return 1;
+ }
+
+ while (PyDict_Next(kw, &pos, &key, 0)) {
#if PY_MAJOR_VERSION < 3
- if (unlikely(!PyString_CheckExact(key)) && unlikely(!PyString_Check(key)))
+ if (unlikely(!PyString_Check(key)))
#endif
if (unlikely(!PyUnicode_Check(key)))
goto invalid_keyword_type;
}
- if ((!kw_allowed) && unlikely(key))
+ if (!kw_allowed && unlikely(key))
goto invalid_keyword;
return 1;
invalid_keyword_type:
@@ -160,11 +185,12 @@
return 0;
#endif
invalid_keyword:
- PyErr_Format(PyExc_TypeError,
#if PY_MAJOR_VERSION < 3
+ PyErr_Format(PyExc_TypeError,
"%.200s() got an unexpected keyword argument '%.200s'",
function_name, PyString_AsString(key));
#else
+ PyErr_Format(PyExc_TypeError,
"%s() got an unexpected keyword argument '%U'",
function_name, key);
#endif
@@ -174,17 +200,22 @@
//////////////////// ParseKeywords.proto ////////////////////
-static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], \
- PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, \
+static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject *const *kwvalues,
+ PyObject **argnames[],
+ PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args,
const char* function_name); /*proto*/
//////////////////// ParseKeywords ////////////////////
//@requires: RaiseDoubleKeywords
// __Pyx_ParseOptionalKeywords copies the optional/unknown keyword
-// arguments from the kwds dict into kwds2. If kwds2 is NULL, unknown
+// arguments from kwds into the dict kwds2. If kwds2 is NULL, unknown
// keywords will raise an invalid keyword error.
//
+// When not using METH_FASTCALL, kwds is a dict and kwvalues is NULL.
+// Otherwise, kwds is a tuple with keyword names and kwvalues is a C
+// array with the corresponding values.
+//
// Three kinds of errors are checked: 1) non-string keywords, 2)
// unexpected keywords and 3) overlap with positional arguments.
//
@@ -196,6 +227,7 @@
static int __Pyx_ParseOptionalKeywords(
PyObject *kwds,
+ PyObject *const *kwvalues,
PyObject **argnames[],
PyObject *kwds2,
PyObject *values[],
@@ -206,8 +238,20 @@
Py_ssize_t pos = 0;
PyObject*** name;
PyObject*** first_kw_arg = argnames + num_pos_args;
+ int kwds_is_tuple = CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds));
+
+ while (1) {
+ if (kwds_is_tuple) {
+ if (pos >= PyTuple_GET_SIZE(kwds)) break;
+ key = PyTuple_GET_ITEM(kwds, pos);
+ value = kwvalues[pos];
+ pos++;
+ }
+ else
+ {
+ if (!PyDict_Next(kwds, &pos, &key, &value)) break;
+ }
- while (PyDict_Next(kwds, &pos, &key, &value)) {
name = first_kw_arg;
while (*name && (**name != key)) name++;
if (*name) {
@@ -217,7 +261,7 @@
name = first_kw_arg;
#if PY_MAJOR_VERSION < 3
- if (likely(PyString_CheckExact(key)) || likely(PyString_Check(key))) {
+ if (likely(PyString_Check(key))) {
while (*name) {
if ((CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**name) == PyString_GET_SIZE(key))
&& _PyString_Eq(**name, key)) {
@@ -243,12 +287,13 @@
#endif
if (likely(PyUnicode_Check(key))) {
while (*name) {
- int cmp = (**name == key) ? 0 :
+ int cmp = (
#if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3
- (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
+ (__Pyx_PyUnicode_GET_LENGTH(**name) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 :
#endif
- // need to convert argument name from bytes to unicode for comparison
- PyUnicode_Compare(**name, key);
+ // In Py2, we may need to convert the argument name from str to unicode for comparison.
+ PyUnicode_Compare(**name, key)
+ );
if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
if (cmp == 0) {
values[name-argnames] = value;
@@ -263,7 +308,7 @@
while (argname != first_kw_arg) {
int cmp = (**argname == key) ? 0 :
#if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3
- (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
+ (__Pyx_PyUnicode_GET_LENGTH(**argname) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 :
#endif
// need to convert argument name from bytes to unicode for comparison
PyUnicode_Compare(**argname, key);
@@ -290,11 +335,12 @@
"%.200s() keywords must be strings", function_name);
goto bad;
invalid_keyword:
- PyErr_Format(PyExc_TypeError,
#if PY_MAJOR_VERSION < 3
+ PyErr_Format(PyExc_TypeError,
"%.200s() got an unexpected keyword argument '%.200s'",
function_name, PyString_AsString(key));
#else
+ PyErr_Format(PyExc_TypeError,
"%s() got an unexpected keyword argument '%U'",
function_name, key);
#endif
@@ -320,7 +366,7 @@
if (unlikely(!iter)) {
// slow fallback: try converting to dict, then iterate
PyObject *args;
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad;
+ if (unlikely(!PyErr_ExceptionMatches(PyExc_AttributeError))) goto bad;
PyErr_Clear();
args = PyTuple_Pack(1, source_mapping);
if (likely(args)) {
@@ -356,3 +402,74 @@
Py_XDECREF(iter);
return -1;
}
+
+
+/////////////// fastcall.proto ///////////////
+
+// We define various functions and macros with two variants:
+//..._FASTCALL and ..._VARARGS
+
+// The first is used when METH_FASTCALL is enabled and the second is used
+// otherwise. If the Python implementation does not support METH_FASTCALL
+// (because it's an old version of CPython or it's not CPython at all),
+// then the ..._FASTCALL macros simply alias ..._VARARGS
+
+#define __Pyx_Arg_VARARGS(args, i) PyTuple_GET_ITEM(args, i)
+#define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds)
+#define __Pyx_KwValues_VARARGS(args, nargs) NULL
+#define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s)
+#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw)
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_Arg_FASTCALL(args, i) args[i]
+ #define __Pyx_NumKwargs_FASTCALL(kwds) PyTuple_GET_SIZE(kwds)
+ #define __Pyx_KwValues_FASTCALL(args, nargs) (&args[nargs])
+ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s);
+ #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw)
+#else
+ #define __Pyx_Arg_FASTCALL __Pyx_Arg_VARARGS
+ #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS
+ #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS
+ #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS
+ #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS
+#endif
+
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_ArgsSlice_VARARGS(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_VARARGS(args, start), stop - start)
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_FASTCALL(args, start), stop - start)
+#else
+/* Not CPython, so certainly no METH_FASTCALL support */
+#define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#endif
+
+
+/////////////// fastcall ///////////////
+//@requires: ObjectHandling.c::TupleAndListFromArray
+//@requires: StringTools.c::UnicodeEquals
+
+#if CYTHON_METH_FASTCALL
+// kwnames: tuple with names of keyword arguments
+// kwvalues: C array with values of keyword arguments
+// s: str with the keyword name to look for
+static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s)
+{
+ // Search the kwnames array for s and return the corresponding value.
+ // We do two loops: a first one to compare pointers (which will find a
+ // match if the name in kwnames is interned, given that s is interned
+ // by Cython). A second loop compares the actual strings.
+ Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames);
+ for (i = 0; i < n; i++)
+ {
+ if (s == PyTuple_GET_ITEM(kwnames, i)) return kwvalues[i];
+ }
+ for (i = 0; i < n; i++)
+ {
+ int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ);
+ if (unlikely(eq != 0)) {
+ if (unlikely(eq < 0)) return NULL; // error
+ return kwvalues[i];
+ }
+ }
+ return NULL; // not found (no exception set)
+}
+#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/ImportExport.c cython-0.20.1+1~202203241016-9537/Cython/Utility/ImportExport.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/ImportExport.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/ImportExport.c 2022-03-24 10:16:46.000000000 +0000
@@ -9,6 +9,147 @@
#endif
+/////////////// ImportDottedModule.proto ///////////////
+
+static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple); /*proto*/
+
+/////////////// ImportDottedModule ///////////////
+//@requires: Import
+
+#if PY_MAJOR_VERSION >= 3
+static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts_tuple, Py_ssize_t count) {
+ PyObject *partial_name = NULL, *slice = NULL, *sep = NULL;
+ if (unlikely(PyErr_Occurred())) {
+ PyErr_Clear();
+ }
+ if (likely(PyTuple_GET_SIZE(parts_tuple) == count)) {
+ partial_name = name;
+ } else {
+ slice = PySequence_GetSlice(parts_tuple, 0, count);
+ if (unlikely(!slice))
+ goto bad;
+ sep = PyUnicode_FromStringAndSize(".", 1);
+ if (unlikely(!sep))
+ goto bad;
+ partial_name = PyUnicode_Join(sep, slice);
+ }
+
+ PyErr_Format(
+#if PY_MAJOR_VERSION < 3
+ PyExc_ImportError,
+ "No module named '%s'", PyString_AS_STRING(partial_name));
+#else
+#if PY_VERSION_HEX >= 0x030600B1
+ PyExc_ModuleNotFoundError,
+#else
+ PyExc_ImportError,
+#endif
+ "No module named '%U'", partial_name);
+#endif
+
+bad:
+ Py_XDECREF(sep);
+ Py_XDECREF(slice);
+ Py_XDECREF(partial_name);
+ return NULL;
+}
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) {
+ PyObject *imported_module;
+#if PY_VERSION_HEX < 0x030700A1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400)
+ PyObject *modules = PyImport_GetModuleDict();
+ if (unlikely(!modules))
+ return NULL;
+ imported_module = __Pyx_PyDict_GetItemStr(modules, name);
+ Py_XINCREF(imported_module);
+#else
+ imported_module = PyImport_GetModule(name);
+#endif
+ return imported_module;
+}
+#endif
+
+static PyObject *__Pyx__ImportDottedModule(PyObject *name, PyObject *parts_tuple) {
+#if PY_MAJOR_VERSION < 3
+ PyObject *module, *from_list, *star = PYIDENT("*");
+ CYTHON_UNUSED_VAR(parts_tuple);
+ from_list = PyList_New(1);
+ if (unlikely(!from_list))
+ return NULL;
+ Py_INCREF(star);
+ PyList_SET_ITEM(from_list, 0, star);
+ module = __Pyx_Import(name, from_list, 0);
+ Py_DECREF(from_list);
+ return module;
+#else
+ Py_ssize_t i, nparts;
+ PyObject *imported_module;
+ PyObject *module = __Pyx_Import(name, NULL, 0);
+ if (!parts_tuple || unlikely(!module))
+ return module;
+
+ // Look up module in sys.modules, which is safer than the attribute lookups below.
+ imported_module = __Pyx__ImportDottedModule_Lookup(name);
+ if (likely(imported_module)) {
+ Py_DECREF(module);
+ return imported_module;
+ }
+ PyErr_Clear();
+
+ nparts = PyTuple_GET_SIZE(parts_tuple);
+ for (i=1; i < nparts && module; i++) {
+ PyObject *part, *submodule;
+#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ part = PyTuple_GET_ITEM(parts_tuple, i);
+#else
+ part = PySequence_ITEM(parts_tuple, i);
+#endif
+ submodule = __Pyx_PyObject_GetAttrStrNoError(module, part);
+#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
+ Py_DECREF(part);
+#endif
+ Py_DECREF(module);
+ module = submodule;
+ }
+ if (likely(module))
+ return module;
+ return __Pyx__ImportDottedModule_Error(name, parts_tuple, i);
+#endif
+}
+
+static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030400B1
+ PyObject *module = __Pyx__ImportDottedModule_Lookup(name);
+ if (likely(module)) {
+ // CPython guards against thread-concurrent initialisation in importlib.
+ // In this case, we let PyImport_ImportModuleLevelObject() handle the locking.
+ PyObject *spec = __Pyx_PyObject_GetAttrStrNoError(module, PYIDENT("__spec__"));
+ if (likely(spec)) {
+ PyObject *unsafe = __Pyx_PyObject_GetAttrStrNoError(spec, PYIDENT("_initializing"));
+ if (likely(!unsafe || !__Pyx_PyObject_IsTrue(unsafe))) {
+ Py_DECREF(spec);
+ spec = NULL;
+ }
+ Py_XDECREF(unsafe);
+ }
+ if (likely(!spec)) {
+ // Not in initialisation phase => use modules as is.
+ PyErr_Clear();
+ return module;
+ }
+ Py_DECREF(spec);
+ Py_DECREF(module);
+ } else if (PyErr_Occurred()) {
+ PyErr_Clear();
+ }
+#endif
+
+ return __Pyx__ImportDottedModule(name, parts_tuple);
+}
+
+
/////////////// Import.proto ///////////////
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); /*proto*/
@@ -18,49 +159,39 @@
//@substitute: naming
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) {
- PyObject *empty_list = 0;
PyObject *module = 0;
- PyObject *global_dict = 0;
PyObject *empty_dict = 0;
- PyObject *list;
- #if PY_VERSION_HEX < 0x03030000
+ PyObject *empty_list = 0;
+ #if PY_MAJOR_VERSION < 3
PyObject *py_import;
py_import = __Pyx_PyObject_GetAttrStr($builtins_cname, PYIDENT("__import__"));
- if (!py_import)
+ if (unlikely(!py_import))
goto bad;
- #endif
- if (from_list)
- list = from_list;
- else {
+ if (!from_list) {
empty_list = PyList_New(0);
- if (!empty_list)
+ if (unlikely(!empty_list))
goto bad;
- list = empty_list;
+ from_list = empty_list;
}
- global_dict = PyModule_GetDict($module_cname);
- if (!global_dict)
- goto bad;
+ #endif
empty_dict = PyDict_New();
- if (!empty_dict)
+ if (unlikely(!empty_dict))
goto bad;
{
#if PY_MAJOR_VERSION >= 3
if (level == -1) {
- if (strchr(__Pyx_MODULE_NAME, '.')) {
+ // Avoid C compiler warning if strchr() evaluates to false at compile time.
+ if ((1) && (strchr(__Pyx_MODULE_NAME, '.'))) {
/* try package relative import first */
- #if PY_VERSION_HEX < 0x03030000
- PyObject *py_level = PyInt_FromLong(1);
- if (!py_level)
- goto bad;
- module = PyObject_CallFunctionObjArgs(py_import,
- name, global_dict, empty_dict, list, py_level, NULL);
- Py_DECREF(py_level);
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ module = PyImport_ImportModuleLevelObject(
+ name, empty_dict, empty_dict, from_list, 1);
#else
module = PyImport_ImportModuleLevelObject(
- name, global_dict, empty_dict, list, 1);
+ name, $moddict_cname, empty_dict, from_list, 1);
#endif
- if (!module) {
- if (!PyErr_ExceptionMatches(PyExc_ImportError))
+ if (unlikely(!module)) {
+ if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError)))
goto bad;
PyErr_Clear();
}
@@ -69,25 +200,30 @@
}
#endif
if (!module) {
- #if PY_VERSION_HEX < 0x03030000
+ #if PY_MAJOR_VERSION < 3
PyObject *py_level = PyInt_FromLong(level);
- if (!py_level)
+ if (unlikely(!py_level))
goto bad;
module = PyObject_CallFunctionObjArgs(py_import,
- name, global_dict, empty_dict, list, py_level, NULL);
+ name, $moddict_cname, empty_dict, from_list, py_level, (PyObject *)NULL);
Py_DECREF(py_level);
#else
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ module = PyImport_ImportModuleLevelObject(
+ name, empty_dict, empty_dict, from_list, level);
+ #else
module = PyImport_ImportModuleLevelObject(
- name, global_dict, empty_dict, list, level);
+ name, $moddict_cname, empty_dict, from_list, level);
+ #endif
#endif
}
}
bad:
- #if PY_VERSION_HEX < 0x03030000
+ Py_XDECREF(empty_dict);
+ Py_XDECREF(empty_list);
+ #if PY_MAJOR_VERSION < 3
Py_XDECREF(py_import);
#endif
- Py_XDECREF(empty_list);
- Py_XDECREF(empty_dict);
return module;
}
@@ -102,6 +238,39 @@
static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) {
PyObject* value = __Pyx_PyObject_GetAttrStr(module, name);
if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ // 'name' may refer to a (sub-)module which has not finished initialization
+ // yet, and may not be assigned as an attribute to its parent, so try
+ // finding it by full name.
+ const char* module_name_str = 0;
+ PyObject* module_name = 0;
+ PyObject* module_dot = 0;
+ PyObject* full_name = 0;
+ PyErr_Clear();
+ module_name_str = PyModule_GetName(module);
+ if (unlikely(!module_name_str)) { goto modbad; }
+ module_name = PyUnicode_FromString(module_name_str);
+ if (unlikely(!module_name)) { goto modbad; }
+ module_dot = PyUnicode_Concat(module_name, PYUNICODE("."));
+ if (unlikely(!module_dot)) { goto modbad; }
+ full_name = PyUnicode_Concat(module_dot, name);
+ if (unlikely(!full_name)) { goto modbad; }
+ #if PY_VERSION_HEX < 0x030700A1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400)
+ {
+ PyObject *modules = PyImport_GetModuleDict();
+ if (unlikely(!modules))
+ goto modbad;
+ value = PyObject_GetItem(modules, full_name);
+ }
+ #else
+ value = PyImport_GetModule(full_name);
+ #endif
+
+ modbad:
+ Py_XDECREF(full_name);
+ Py_XDECREF(module_dot);
+ Py_XDECREF(module_name);
+ }
+ if (unlikely(!value)) {
PyErr_Format(PyExc_ImportError,
#if PY_MAJOR_VERSION < 3
"cannot import name %.230s", PyString_AS_STRING(name));
@@ -160,11 +329,12 @@
}
if (skip_leading_underscores &&
#if PY_MAJOR_VERSION < 3
- PyString_Check(name) &&
+ likely(PyString_Check(name)) &&
PyString_AS_STRING(name)[0] == '_')
#else
- PyUnicode_Check(name) &&
- PyUnicode_AS_UNICODE(name)[0] == '_')
+ likely(PyUnicode_Check(name)) &&
+ likely(__Pyx_PyUnicode_GET_LENGTH(name)) &&
+ __Pyx_PyUnicode_READ_CHAR(name, 0) == '_')
#endif
{
Py_DECREF(name);
@@ -231,35 +401,10 @@
}
-/////////////// ModuleImport.proto ///////////////
-
-static PyObject *__Pyx_ImportModule(const char *name); /*proto*/
-
-/////////////// ModuleImport ///////////////
-//@requires: PyIdentifierFromString
-
-#ifndef __PYX_HAVE_RT_ImportModule
-#define __PYX_HAVE_RT_ImportModule
-static PyObject *__Pyx_ImportModule(const char *name) {
- PyObject *py_name = 0;
- PyObject *py_module = 0;
-
- py_name = __Pyx_PyIdentifier_FromString(name);
- if (!py_name)
- goto bad;
- py_module = PyImport_Import(py_name);
- Py_DECREF(py_name);
- return py_module;
-bad:
- Py_XDECREF(py_name);
- return 0;
-}
-#endif
-
-
/////////////// SetPackagePathFromImportLib.proto ///////////////
-#if PY_VERSION_HEX >= 0x03030000
+// PY_VERSION_HEX >= 0x03030000
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_PEP489_MULTI_PHASE_INIT
static int __Pyx_SetPackagePathFromImportLib(const char* parent_package_name, PyObject *module_name);
#else
#define __Pyx_SetPackagePathFromImportLib(a, b) 0
@@ -269,7 +414,8 @@
//@requires: ObjectHandling.c::PyObjectGetAttrStr
//@substitute: naming
-#if PY_VERSION_HEX >= 0x03030000
+// PY_VERSION_HEX >= 0x03030000
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_PEP489_MULTI_PHASE_INIT
static int __Pyx_SetPackagePathFromImportLib(const char* parent_package_name, PyObject *module_name) {
PyObject *importlib, *loader, *osmod, *ossep, *parts, *package_path;
PyObject *path = NULL, *file_path = NULL;
@@ -341,37 +487,34 @@
/////////////// TypeImport.proto ///////////////
-static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, size_t size, int strict); /*proto*/
+#ifndef __PYX_HAVE_RT_ImportType_proto
+#define __PYX_HAVE_RT_ImportType_proto
+
+enum __Pyx_ImportType_CheckSize {
+ __Pyx_ImportType_CheckSize_Error = 0,
+ __Pyx_ImportType_CheckSize_Warn = 1,
+ __Pyx_ImportType_CheckSize_Ignore = 2
+};
+
+static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name, const char *class_name, size_t size, enum __Pyx_ImportType_CheckSize check_size); /*proto*/
+
+#endif
/////////////// TypeImport ///////////////
-//@requires: PyIdentifierFromString
-//@requires: ModuleImport
#ifndef __PYX_HAVE_RT_ImportType
#define __PYX_HAVE_RT_ImportType
-static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name,
- size_t size, int strict)
+static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name, const char *class_name,
+ size_t size, enum __Pyx_ImportType_CheckSize check_size)
{
- PyObject *py_module = 0;
PyObject *result = 0;
- PyObject *py_name = 0;
char warning[200];
Py_ssize_t basicsize;
-#ifdef Py_LIMITED_API
+#if CYTHON_COMPILING_IN_LIMITED_API
PyObject *py_basicsize;
#endif
- py_module = __Pyx_ImportModule(module_name);
- if (!py_module)
- goto bad;
- py_name = __Pyx_PyIdentifier_FromString(class_name);
- if (!py_name)
- goto bad;
- result = PyObject_GetAttr(py_module, py_name);
- Py_DECREF(py_name);
- py_name = 0;
- Py_DECREF(py_module);
- py_module = 0;
+ result = PyObject_GetAttrString(module, class_name);
if (!result)
goto bad;
if (!PyType_Check(result)) {
@@ -380,7 +523,7 @@
module_name, class_name);
goto bad;
}
-#ifndef Py_LIMITED_API
+#if !CYTHON_COMPILING_IN_LIMITED_API
basicsize = ((PyTypeObject *)result)->tp_basicsize;
#else
py_basicsize = PyObject_GetAttrString(result, "__basicsize__");
@@ -392,21 +535,30 @@
if (basicsize == (Py_ssize_t)-1 && PyErr_Occurred())
goto bad;
#endif
- if (!strict && (size_t)basicsize > size) {
- PyOS_snprintf(warning, sizeof(warning),
- "%s.%s size changed, may indicate binary incompatibility. Expected %zd, got %zd",
- module_name, class_name, basicsize, size);
- if (PyErr_WarnEx(NULL, warning, 0) < 0) goto bad;
+ if ((size_t)basicsize < size) {
+ PyErr_Format(PyExc_ValueError,
+ "%.200s.%.200s size changed, may indicate binary incompatibility. "
+ "Expected %zd from C header, got %zd from PyObject",
+ module_name, class_name, size, basicsize);
+ goto bad;
}
- else if ((size_t)basicsize != size) {
+ if (check_size == __Pyx_ImportType_CheckSize_Error && (size_t)basicsize != size) {
PyErr_Format(PyExc_ValueError,
- "%.200s.%.200s has the wrong size, try recompiling. Expected %zd, got %zd",
- module_name, class_name, basicsize, size);
+ "%.200s.%.200s size changed, may indicate binary incompatibility. "
+ "Expected %zd from C header, got %zd from PyObject",
+ module_name, class_name, size, basicsize);
goto bad;
}
+ else if (check_size == __Pyx_ImportType_CheckSize_Warn && (size_t)basicsize > size) {
+ PyOS_snprintf(warning, sizeof(warning),
+ "%s.%s size changed, may indicate binary incompatibility. "
+ "Expected %zd from C header, got %zd from PyObject",
+ module_name, class_name, size, basicsize);
+ if (PyErr_WarnEx(NULL, warning, 0) < 0) goto bad;
+ }
+ /* check_size == __Pyx_ImportType_CheckSize_Ignore does not warn nor error */
return (PyTypeObject *)result;
bad:
- Py_XDECREF(py_module);
Py_XDECREF(result);
return NULL;
}
@@ -439,7 +591,6 @@
PyModule_GetName(module), funcname);
goto bad;
}
-#if PY_VERSION_HEX >= 0x02070000
if (!PyCapsule_IsValid(cobj, sig)) {
PyErr_Format(PyExc_TypeError,
"C function %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
@@ -447,21 +598,6 @@
goto bad;
}
tmp.p = PyCapsule_GetPointer(cobj, sig);
-#else
- {const char *desc, *s1, *s2;
- desc = (const char *)PyCObject_GetDesc(cobj);
- if (!desc)
- goto bad;
- s1 = desc; s2 = sig;
- while (*s1 != '\0' && *s1 == *s2) { s1++; s2++; }
- if (*s1 != *s2) {
- PyErr_Format(PyExc_TypeError,
- "C function %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
- PyModule_GetName(module), funcname, sig, desc);
- goto bad;
- }
- tmp.p = PyCObject_AsVoidPtr(cobj);}
-#endif
*f = tmp.fp;
if (!(*f))
goto bad;
@@ -499,11 +635,7 @@
goto bad;
}
tmp.fp = f;
-#if PY_VERSION_HEX >= 0x02070000
cobj = PyCapsule_New(tmp.p, sig, 0);
-#else
- cobj = PyCObject_FromVoidPtrAndDesc(tmp.p, (void *)sig, 0);
-#endif
if (!cobj)
goto bad;
if (PyDict_SetItemString(d, name, cobj) < 0)
@@ -540,7 +672,6 @@
PyModule_GetName(module), name);
goto bad;
}
-#if PY_VERSION_HEX >= 0x02070000
if (!PyCapsule_IsValid(cobj, sig)) {
PyErr_Format(PyExc_TypeError,
"C variable %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
@@ -548,21 +679,6 @@
goto bad;
}
*p = PyCapsule_GetPointer(cobj, sig);
-#else
- {const char *desc, *s1, *s2;
- desc = (const char *)PyCObject_GetDesc(cobj);
- if (!desc)
- goto bad;
- s1 = desc; s2 = sig;
- while (*s1 != '\0' && *s1 == *s2) { s1++; s2++; }
- if (*s1 != *s2) {
- PyErr_Format(PyExc_TypeError,
- "C variable %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
- PyModule_GetName(module), name, sig, desc);
- goto bad;
- }
- *p = PyCObject_AsVoidPtr(cobj);}
-#endif
if (!(*p))
goto bad;
Py_DECREF(d);
@@ -594,11 +710,7 @@
if (__Pyx_PyObject_SetAttrStr($module_cname, PYIDENT("$api_name"), d) < 0)
goto bad;
}
-#if PY_VERSION_HEX >= 0x02070000
cobj = PyCapsule_New(p, sig, 0);
-#else
- cobj = PyCObject_FromVoidPtrAndDesc(p, (void *)sig, 0);
-#endif
if (!cobj)
goto bad;
if (PyDict_SetItem(d, name, cobj) < 0)
@@ -615,19 +727,19 @@
/////////////// SetVTable.proto ///////////////
-static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/
+static int __Pyx_SetVtable(PyTypeObject* typeptr , void* vtable); /*proto*/
/////////////// SetVTable ///////////////
-static int __Pyx_SetVtable(PyObject *dict, void *vtable) {
-#if PY_VERSION_HEX >= 0x02070000
+static int __Pyx_SetVtable(PyTypeObject *type, void *vtable) {
PyObject *ob = PyCapsule_New(vtable, 0, 0);
+ if (unlikely(!ob))
+ goto bad;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(PyObject_SetAttr((PyObject *) type, PYIDENT("__pyx_vtable__"), ob) < 0))
#else
- PyObject *ob = PyCObject_FromVoidPtr(vtable, 0);
+ if (unlikely(PyDict_SetItem(type->tp_dict, PYIDENT("__pyx_vtable__"), ob) < 0))
#endif
- if (!ob)
- goto bad;
- if (PyDict_SetItem(dict, PYIDENT("__pyx_vtable__"), ob) < 0)
goto bad;
Py_DECREF(ob);
return 0;
@@ -639,20 +751,20 @@
/////////////// GetVTable.proto ///////////////
-static void* __Pyx_GetVtable(PyObject *dict); /*proto*/
+static void* __Pyx_GetVtable(PyTypeObject *type); /*proto*/
/////////////// GetVTable ///////////////
-static void* __Pyx_GetVtable(PyObject *dict) {
+static void* __Pyx_GetVtable(PyTypeObject *type) {
void* ptr;
- PyObject *ob = PyObject_GetItem(dict, PYIDENT("__pyx_vtable__"));
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *ob = PyObject_GetAttr((PyObject *)type, PYIDENT("__pyx_vtable__"));
+#else
+ PyObject *ob = PyObject_GetItem(type->tp_dict, PYIDENT("__pyx_vtable__"));
+#endif
if (!ob)
goto bad;
-#if PY_VERSION_HEX >= 0x02070000
ptr = PyCapsule_GetPointer(ob, 0);
-#else
- ptr = PyCObject_AsVoidPtr(ob);
-#endif
if (!ptr && !PyErr_Occurred())
PyErr_SetString(PyExc_RuntimeError, "invalid vtable found for imported type");
Py_DECREF(ob);
@@ -661,3 +773,113 @@
Py_XDECREF(ob);
return NULL;
}
+
+
+/////////////// MergeVTables.proto ///////////////
+//@requires: GetVTable
+
+// TODO: find a way to make this work with the Limited API!
+#if !CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_MergeVtables(PyTypeObject *type); /*proto*/
+#endif
+
+/////////////// MergeVTables ///////////////
+
+#if !CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_MergeVtables(PyTypeObject *type) {
+ int i;
+ void** base_vtables;
+ __Pyx_TypeName tp_base_name;
+ __Pyx_TypeName base_name;
+ void* unknown = (void*)-1;
+ PyObject* bases = type->tp_bases;
+ int base_depth = 0;
+ {
+ PyTypeObject* base = type->tp_base;
+ while (base) {
+ base_depth += 1;
+ base = base->tp_base;
+ }
+ }
+ base_vtables = (void**) malloc(sizeof(void*) * (size_t)(base_depth + 1));
+ base_vtables[0] = unknown;
+ // Could do MRO resolution of individual methods in the future, assuming
+ // compatible vtables, but for now simply require a common vtable base.
+ // Note that if the vtables of various bases are extended separately,
+ // resolution isn't possible and we must reject it just as when the
+ // instance struct is so extended. (It would be good to also do this
+ // check when a multiple-base class is created in pure Python as well.)
+ for (i = 1; i < PyTuple_GET_SIZE(bases); i++) {
+ void* base_vtable = __Pyx_GetVtable(((PyTypeObject*)PyTuple_GET_ITEM(bases, i)));
+ if (base_vtable != NULL) {
+ int j;
+ PyTypeObject* base = type->tp_base;
+ for (j = 0; j < base_depth; j++) {
+ if (base_vtables[j] == unknown) {
+ base_vtables[j] = __Pyx_GetVtable(base);
+ base_vtables[j + 1] = unknown;
+ }
+ if (base_vtables[j] == base_vtable) {
+ break;
+ } else if (base_vtables[j] == NULL) {
+ // No more potential matching bases (with vtables).
+ goto bad;
+ }
+ base = base->tp_base;
+ }
+ }
+ }
+ PyErr_Clear();
+ free(base_vtables);
+ return 0;
+bad:
+ tp_base_name = __Pyx_PyType_GetName(type->tp_base);
+ base_name = __Pyx_PyType_GetName((PyTypeObject*)PyTuple_GET_ITEM(bases, i));
+ PyErr_Format(PyExc_TypeError,
+ "multiple bases have vtable conflict: '" __Pyx_FMT_TYPENAME "' and '" __Pyx_FMT_TYPENAME "'", tp_base_name, base_name);
+ __Pyx_DECREF_TypeName(tp_base_name);
+ __Pyx_DECREF_TypeName(base_name);
+ free(base_vtables);
+ return -1;
+}
+#endif
+
+
+/////////////// ImportNumPyArray.proto ///////////////
+
+static PyObject *__pyx_numpy_ndarray = NULL;
+
+static PyObject* __Pyx_ImportNumPyArrayTypeIfAvailable(void); /*proto*/
+
+/////////////// ImportNumPyArray.cleanup ///////////////
+Py_CLEAR(__pyx_numpy_ndarray);
+
+/////////////// ImportNumPyArray ///////////////
+//@requires: ImportExport.c::Import
+
+static PyObject* __Pyx__ImportNumPyArray(void) {
+ PyObject *numpy_module, *ndarray_object = NULL;
+ numpy_module = __Pyx_Import(PYIDENT("numpy"), NULL, 0);
+ if (likely(numpy_module)) {
+ ndarray_object = PyObject_GetAttrString(numpy_module, "ndarray");
+ Py_DECREF(numpy_module);
+ }
+ if (unlikely(!ndarray_object)) {
+ // ImportError, AttributeError, ...
+ PyErr_Clear();
+ }
+ if (unlikely(!ndarray_object || !PyObject_TypeCheck(ndarray_object, &PyType_Type))) {
+ Py_XDECREF(ndarray_object);
+ Py_INCREF(Py_None);
+ ndarray_object = Py_None;
+ }
+ return ndarray_object;
+}
+
+static CYTHON_INLINE PyObject* __Pyx_ImportNumPyArrayTypeIfAvailable(void) {
+ if (unlikely(!__pyx_numpy_ndarray)) {
+ __pyx_numpy_ndarray = __Pyx__ImportNumPyArray();
+ }
+ Py_INCREF(__pyx_numpy_ndarray);
+ return __pyx_numpy_ndarray;
+}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/MemoryView_C.c cython-0.20.1+1~202203241016-9537/Cython/Utility/MemoryView_C.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/MemoryView_C.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/MemoryView_C.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,4 +1,5 @@
////////// MemviewSliceStruct.proto //////////
+//@proto_block: utility_code_proto_before_types
/* memoryview slice struct */
struct {{memview_struct_name}};
@@ -11,8 +12,12 @@
Py_ssize_t suboffsets[{{max_dims}}];
} {{memviewslice_name}};
+// used for "len(memviewslice)"
+#define __Pyx_MemoryView_Len(m) (m.shape[0])
+
/////////// Atomics.proto /////////////
+//@proto_block: utility_code_proto_before_types
#include
@@ -77,7 +82,7 @@
/////////////// ObjectToMemviewSlice.proto ///////////////
-static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *);
+static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *, int writable_flag);
////////// MemviewSliceInit.proto //////////
@@ -108,9 +113,9 @@
#define __pyx_get_slice_count_pointer(memview) (memview->acquisition_count_aligned_p)
#define __pyx_get_slice_count(memview) (*__pyx_get_slice_count_pointer(memview))
#define __PYX_INC_MEMVIEW(slice, have_gil) __Pyx_INC_MEMVIEW(slice, have_gil, __LINE__)
-#define __PYX_XDEC_MEMVIEW(slice, have_gil) __Pyx_XDEC_MEMVIEW(slice, have_gil, __LINE__)
+#define __PYX_XCLEAR_MEMVIEW(slice, have_gil) __Pyx_XCLEAR_MEMVIEW(slice, have_gil, __LINE__)
static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int);
-static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int);
+static CYTHON_INLINE void __Pyx_XCLEAR_MEMVIEW({{memviewslice_name}} *, int, int);
/////////////// MemviewSliceIndex.proto ///////////////
@@ -122,7 +127,7 @@
/////////////// ObjectToMemviewSlice ///////////////
//@requires: MemviewSliceValidateAndInit
-static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) {
+static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj, int writable_flag) {
{{memviewslice_name}} result = {{memslice_init}};
__Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}];
int axes_specs[] = { {{axes_specs}} };
@@ -135,7 +140,7 @@
}
retcode = __Pyx_ValidateAndInit_memviewslice(axes_specs, {{c_or_f_flag}},
- {{buf_flag}}, {{ndim}},
+ {{buf_flag}} | writable_flag, {{ndim}},
&{{dtype_typeinfo}}, stack,
&result, obj);
@@ -164,6 +169,8 @@
/////////////// MemviewSliceValidateAndInit ///////////////
//@requires: Buffer.c::TypeInfoCompare
+//@requires: Buffer.c::BufferFormatStructs
+//@requires: Buffer.c::BufferFormatCheck
static int
__pyx_check_strides(Py_buffer *buf, int dim, int ndim, int spec)
@@ -174,13 +181,13 @@
if (buf->strides) {
if (spec & __Pyx_MEMVIEW_CONTIG) {
if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL)) {
- if (buf->strides[dim] != sizeof(void *)) {
+ if (unlikely(buf->strides[dim] != sizeof(void *))) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly contiguous "
"in dimension %d.", dim);
goto fail;
}
- } else if (buf->strides[dim] != buf->itemsize) {
+ } else if (unlikely(buf->strides[dim] != buf->itemsize)) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous "
"in the same dimension.");
@@ -192,7 +199,7 @@
Py_ssize_t stride = buf->strides[dim];
if (stride < 0)
stride = -stride;
- if (stride < buf->itemsize) {
+ if (unlikely(stride < buf->itemsize)) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous "
"in the same dimension.");
@@ -200,17 +207,17 @@
}
}
} else {
- if (spec & __Pyx_MEMVIEW_CONTIG && dim != ndim - 1) {
+ if (unlikely(spec & __Pyx_MEMVIEW_CONTIG && dim != ndim - 1)) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not contiguous in "
"dimension %d", dim);
goto fail;
- } else if (spec & (__Pyx_MEMVIEW_PTR)) {
+ } else if (unlikely(spec & (__Pyx_MEMVIEW_PTR))) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not indirect in "
"dimension %d", dim);
goto fail;
- } else if (buf->suboffsets) {
+ } else if (unlikely(buf->suboffsets)) {
PyErr_SetString(PyExc_ValueError,
"Buffer exposes suboffsets but no strides");
goto fail;
@@ -223,12 +230,13 @@
}
static int
-__pyx_check_suboffsets(Py_buffer *buf, int dim, CYTHON_UNUSED int ndim, int spec)
+__pyx_check_suboffsets(Py_buffer *buf, int dim, int ndim, int spec)
{
+ CYTHON_UNUSED_VAR(ndim);
// Todo: without PyBUF_INDIRECT we may not have suboffset information, i.e., the
// ptr may not be set to NULL but may be uninitialized?
if (spec & __Pyx_MEMVIEW_DIRECT) {
- if (buf->suboffsets && buf->suboffsets[dim] >= 0) {
+ if (unlikely(buf->suboffsets && buf->suboffsets[dim] >= 0)) {
PyErr_Format(PyExc_ValueError,
"Buffer not compatible with direct access "
"in dimension %d.", dim);
@@ -237,7 +245,7 @@
}
if (spec & __Pyx_MEMVIEW_PTR) {
- if (!buf->suboffsets || (buf->suboffsets && buf->suboffsets[dim] < 0)) {
+ if (unlikely(!buf->suboffsets || (buf->suboffsets[dim] < 0))) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly accessible "
"in dimension %d.", dim);
@@ -258,9 +266,7 @@
if (c_or_f_flag & __Pyx_IS_F_CONTIG) {
Py_ssize_t stride = 1;
for (i = 0; i < ndim; i++) {
- if (stride * buf->itemsize != buf->strides[i] &&
- buf->shape[i] > 1)
- {
+ if (unlikely(stride * buf->itemsize != buf->strides[i] && buf->shape[i] > 1)) {
PyErr_SetString(PyExc_ValueError,
"Buffer not fortran contiguous.");
goto fail;
@@ -270,8 +276,7 @@
} else if (c_or_f_flag & __Pyx_IS_C_CONTIG) {
Py_ssize_t stride = 1;
for (i = ndim - 1; i >- 1; i--) {
- if (stride * buf->itemsize != buf->strides[i] &&
- buf->shape[i] > 1) {
+ if (unlikely(stride * buf->itemsize != buf->strides[i] && buf->shape[i] > 1)) {
PyErr_SetString(PyExc_ValueError,
"Buffer not C contiguous.");
goto fail;
@@ -318,7 +323,7 @@
}
buf = &memview->view;
- if (buf->ndim != ndim) {
+ if (unlikely(buf->ndim != ndim)) {
PyErr_Format(PyExc_ValueError,
"Buffer has wrong number of dimensions (expected %d, got %d)",
ndim, buf->ndim);
@@ -327,10 +332,10 @@
if (new_memview) {
__Pyx_BufFmt_Init(&ctx, stack, dtype);
- if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
+ if (unlikely(!__Pyx_BufFmt_CheckString(&ctx, buf->format))) goto fail;
}
- if ((unsigned) buf->itemsize != dtype->size) {
+ if (unlikely((unsigned) buf->itemsize != dtype->size)) {
PyErr_Format(PyExc_ValueError,
"Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "u byte%s) "
"does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "u byte%s)",
@@ -343,18 +348,22 @@
}
/* Check axes */
- for (i = 0; i < ndim; i++) {
- spec = axes_specs[i];
- if (!__pyx_check_strides(buf, i, ndim, spec))
- goto fail;
- if (!__pyx_check_suboffsets(buf, i, ndim, spec))
+ if (buf->len > 0) {
+ // 0-sized arrays do not undergo these checks since their strides are
+ // irrelevant and they are always both C- and F-contiguous.
+ for (i = 0; i < ndim; i++) {
+ spec = axes_specs[i];
+ if (unlikely(!__pyx_check_strides(buf, i, ndim, spec)))
+ goto fail;
+ if (unlikely(!__pyx_check_suboffsets(buf, i, ndim, spec)))
+ goto fail;
+ }
+
+ /* Check contiguity */
+ if (unlikely(buf->strides && !__pyx_verify_contig(buf, ndim, c_or_f_flag)))
goto fail;
}
- /* Check contiguity */
- if (buf->strides && !__pyx_verify_contig(buf, ndim, c_or_f_flag))
- goto fail;
-
/* Initialize */
if (unlikely(__Pyx_init_memviewslice(memview, ndim, memviewslice,
new_memview != NULL) == -1)) {
@@ -387,11 +396,7 @@
Py_buffer *buf = &memview->view;
__Pyx_RefNannySetupContext("init_memviewslice", 0);
- if (!buf) {
- PyErr_SetString(PyExc_ValueError,
- "buf is NULL.");
- goto fail;
- } else if (memviewslice->memview || memviewslice->data) {
+ if (unlikely(memviewslice->memview || memviewslice->data)) {
PyErr_SetString(PyExc_ValueError,
"memviewslice is already initialized!");
goto fail;
@@ -437,8 +442,12 @@
return retval;
}
+#ifndef Py_NO_RETURN
+// available since Py3.3
+#define Py_NO_RETURN
+#endif
-static CYTHON_INLINE void __pyx_fatalerror(const char *fmt, ...) {
+static void __pyx_fatalerror(const char *fmt, ...) Py_NO_RETURN {
va_list vargs;
char msg[200];
@@ -447,11 +456,10 @@
#else
va_start(vargs);
#endif
-
vsnprintf(msg, 200, fmt, vargs);
- Py_FatalError(msg);
-
va_end(vargs);
+
+ Py_FatalError(msg);
}
static CYTHON_INLINE int
@@ -480,47 +488,49 @@
static CYTHON_INLINE void
__Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil, int lineno)
{
- int first_time;
+ __pyx_atomic_int_type old_acquisition_count;
struct {{memview_struct_name}} *memview = memslice->memview;
- if (!memview || (PyObject *) memview == Py_None)
- return; /* allow uninitialized memoryview assignment */
-
- if (__pyx_get_slice_count(memview) < 0)
- __pyx_fatalerror("Acquisition count is %d (line %d)",
- __pyx_get_slice_count(memview), lineno);
-
- first_time = __pyx_add_acquisition_count(memview) == 0;
+ if (unlikely(!memview || (PyObject *) memview == Py_None)) {
+ // Allow uninitialized memoryview assignment and do not ref-count None.
+ return;
+ }
- if (first_time) {
- if (have_gil) {
- Py_INCREF((PyObject *) memview);
+ old_acquisition_count = __pyx_add_acquisition_count(memview);
+ if (unlikely(old_acquisition_count <= 0)) {
+ if (likely(old_acquisition_count == 0)) {
+ // First acquisition => keep the memoryview object alive.
+ if (have_gil) {
+ Py_INCREF((PyObject *) memview);
+ } else {
+ PyGILState_STATE _gilstate = PyGILState_Ensure();
+ Py_INCREF((PyObject *) memview);
+ PyGILState_Release(_gilstate);
+ }
} else {
- PyGILState_STATE _gilstate = PyGILState_Ensure();
- Py_INCREF((PyObject *) memview);
- PyGILState_Release(_gilstate);
+ __pyx_fatalerror("Acquisition count is %d (line %d)",
+ __pyx_get_slice_count(memview), lineno);
}
}
}
-static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
+static CYTHON_INLINE void __Pyx_XCLEAR_MEMVIEW({{memviewslice_name}} *memslice,
int have_gil, int lineno) {
- int last_time;
+ __pyx_atomic_int_type old_acquisition_count;
struct {{memview_struct_name}} *memview = memslice->memview;
- if (!memview ) {
- return;
- } else if ((PyObject *) memview == Py_None) {
+ if (unlikely(!memview || (PyObject *) memview == Py_None)) {
+ // Do not ref-count None.
memslice->memview = NULL;
return;
}
- if (__pyx_get_slice_count(memview) <= 0)
- __pyx_fatalerror("Acquisition count is %d (line %d)",
- __pyx_get_slice_count(memview), lineno);
-
- last_time = __pyx_sub_acquisition_count(memview) == 1;
+ old_acquisition_count = __pyx_sub_acquisition_count(memview);
memslice->data = NULL;
- if (last_time) {
+ if (likely(old_acquisition_count > 1)) {
+ // Still other slices out there => we do not own the reference.
+ memslice->memview = NULL;
+ } else if (likely(old_acquisition_count == 1)) {
+ // Last slice => discard owned Python reference to memoryview object.
if (have_gil) {
Py_CLEAR(memslice->memview);
} else {
@@ -529,7 +539,8 @@
PyGILState_Release(_gilstate);
}
} else {
- memslice->memview = NULL;
+ __pyx_fatalerror("Acquisition count is %d (line %d)",
+ __pyx_get_slice_count(memview), lineno);
}
}
@@ -564,7 +575,7 @@
__Pyx_RefNannySetupContext("__pyx_memoryview_copy_new_contig", 0);
for (i = 0; i < ndim; i++) {
- if (from_mvs->suboffsets[i] >= 0) {
+ if (unlikely(from_mvs->suboffsets[i] >= 0)) {
PyErr_Format(PyExc_ValueError, "Cannot copy memoryview slice with "
"indirect dimensions (axis %d)", i);
goto fail;
@@ -689,29 +700,21 @@
}
-////////// MemviewSliceIsCContig.proto //////////
-
-#define __pyx_memviewslice_is_c_contig{{ndim}}(slice) \
- __pyx_memviewslice_is_contig(slice, 'C', {{ndim}})
-
+////////// MemviewSliceCheckContig.proto //////////
-////////// MemviewSliceIsFContig.proto //////////
-
-#define __pyx_memviewslice_is_f_contig{{ndim}}(slice) \
- __pyx_memviewslice_is_contig(slice, 'F', {{ndim}})
+#define __pyx_memviewslice_is_contig_{{contig_type}}{{ndim}}(slice) \
+ __pyx_memviewslice_is_contig(slice, '{{contig_type}}', {{ndim}})
////////// MemviewSliceIsContig.proto //////////
-static int __pyx_memviewslice_is_contig(const {{memviewslice_name}} mvs,
- char order, int ndim);
+static int __pyx_memviewslice_is_contig(const {{memviewslice_name}} mvs, char order, int ndim);/*proto*/
////////// MemviewSliceIsContig //////////
static int
-__pyx_memviewslice_is_contig(const {{memviewslice_name}} mvs,
- char order, int ndim)
+__pyx_memviewslice_is_contig(const {{memviewslice_name}} mvs, char order, int ndim)
{
int i, index, step, start;
Py_ssize_t itemsize = mvs.memview->view.itemsize;
@@ -775,7 +778,7 @@
{{if from_py_function}}
static CYTHON_INLINE int {{set_function}}(const char *itemp, PyObject *obj) {
{{dtype}} value = {{from_py_function}}(obj);
- if ({{error_condition}})
+ if (unlikely({{error_condition}}))
return 0;
*({{dtype}} *) itemp = value;
return 1;
@@ -850,28 +853,37 @@
{
Py_ssize_t __pyx_tmp_idx = {{idx}};
- Py_ssize_t __pyx_tmp_shape = {{src}}.shape[{{dim}}];
+
+ {{if wraparound or boundscheck}}
+ Py_ssize_t __pyx_tmp_shape = {{src}}.shape[{{dim}}];
+ {{endif}}
+
Py_ssize_t __pyx_tmp_stride = {{src}}.strides[{{dim}}];
- if ({{wraparound}} && (__pyx_tmp_idx < 0))
- __pyx_tmp_idx += __pyx_tmp_shape;
+ {{if wraparound}}
+ if (__pyx_tmp_idx < 0)
+ __pyx_tmp_idx += __pyx_tmp_shape;
+ {{endif}}
+
+ {{if boundscheck}}
+ if (unlikely(!__Pyx_is_valid_index(__pyx_tmp_idx, __pyx_tmp_shape))) {
+ {{if not have_gil}}
+ #ifdef WITH_THREAD
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
+ #endif
+ {{endif}}
- if ({{boundscheck}} && (__pyx_tmp_idx < 0 || __pyx_tmp_idx >= __pyx_tmp_shape)) {
- {{if not have_gil}}
- #ifdef WITH_THREAD
- PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
- #endif
- {{endif}}
-
- PyErr_SetString(PyExc_IndexError, "Index out of bounds (axis {{dim}})");
-
- {{if not have_gil}}
- #ifdef WITH_THREAD
- PyGILState_Release(__pyx_gilstate_save);
- #endif
- {{endif}}
+ PyErr_SetString(PyExc_IndexError,
+ "Index out of bounds (axis {{dim}})");
- {{error_goto}}
- }
+ {{if not have_gil}}
+ #ifdef WITH_THREAD
+ PyGILState_Release(__pyx_gilstate_save);
+ #endif
+ {{endif}}
+
+ {{error_goto}}
+ }
+ {{endif}}
{{if all_dimensions_direct}}
{{dst}}.data += __pyx_tmp_idx * __pyx_tmp_stride;
@@ -917,7 +929,7 @@
////////// FillStrided1DScalar //////////
/* Fill a slice with a scalar value. The dimension is direct and strided or contiguous */
-/* This can be used as a callback for the memoryview object to efficienty assign a scalar */
+/* This can be used as a callback for the memoryview object to efficiently assign a scalar */
/* Currently unused */
static void
__pyx_fill_slice_{{dtype_name}}({{type_decl}} *p, Py_ssize_t extent, Py_ssize_t stride,
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/MemoryView.pyx cython-0.20.1+1~202203241016-9537/Cython/Utility/MemoryView.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/MemoryView.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/MemoryView.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -1,5 +1,8 @@
#################### View.MemoryView ####################
+# cython: language_level=3str
+# cython: binding=False
+
# This utility provides cython.array and cython.view.memoryview
from __future__ import absolute_import
@@ -8,8 +11,12 @@
# from cpython cimport ...
cdef extern from "Python.h":
+ ctypedef struct PyObject
int PyIndex_Check(object)
object PyLong_FromVoidPtr(void *)
+ PyObject *PyExc_IndexError
+ PyObject *PyExc_ValueError
+ PyObject *PyExc_MemoryError
cdef extern from "pythread.h":
ctypedef void *PyThread_type_lock
@@ -49,7 +56,7 @@
Py_ssize_t suboffsets[{{max_dims}}]
void __PYX_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
- void __PYX_XDEC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
+ void __PYX_XCLEAR_MEMVIEW({{memviewslice_name}} *memslice, int have_gil)
ctypedef struct __pyx_buffer "Py_buffer":
PyObject *obj
@@ -64,15 +71,13 @@
PyBUF_WRITABLE
PyBUF_STRIDES
PyBUF_INDIRECT
+ PyBUF_ND
PyBUF_RECORDS
+ PyBUF_RECORDS_RO
ctypedef struct __Pyx_TypeInfo:
pass
- cdef object capsule "__pyx_capsule_create" (void *p, char *sig)
- cdef int __pyx_array_getbuffer(PyObject *obj, Py_buffer view, int flags)
- cdef int __pyx_memoryview_getbuffer(PyObject *obj, Py_buffer view, int flags)
-
cdef extern from *:
ctypedef int __pyx_atomic_int
{{memviewslice_name}} slice_copy_contig "__pyx_memoryview_copy_new_contig"(
@@ -92,9 +97,6 @@
void free(void *) nogil
void *memcpy(void *dest, void *src, size_t n) nogil
-
-
-
#
### cython.array class
#
@@ -121,17 +123,16 @@
mode="c", bint allocate_buffer=True):
cdef int idx
- cdef Py_ssize_t i, dim
- cdef PyObject **p
+ cdef Py_ssize_t dim
self.ndim = len(shape)
self.itemsize = itemsize
if not self.ndim:
- raise ValueError("Empty shape tuple for cython.array")
+ raise ValueError, "Empty shape tuple for cython.array"
if itemsize <= 0:
- raise ValueError("itemsize <= 0 for cython.array")
+ raise ValueError, "itemsize <= 0 for cython.array"
if not isinstance(format, bytes):
format = format.encode('ASCII')
@@ -143,76 +144,66 @@
self._strides = self._shape + self.ndim
if not self._shape:
- raise MemoryError("unable to allocate shape and strides.")
+ raise MemoryError, "unable to allocate shape and strides."
# cdef Py_ssize_t dim, stride
for idx, dim in enumerate(shape):
if dim <= 0:
- raise ValueError("Invalid shape in axis %d: %d." % (idx, dim))
+ raise ValueError, f"Invalid shape in axis {idx}: {dim}."
self._shape[idx] = dim
cdef char order
- if mode == 'fortran':
- order = b'F'
- self.mode = u'fortran'
- elif mode == 'c':
+ if mode == 'c':
order = b'C'
self.mode = u'c'
+ elif mode == 'fortran':
+ order = b'F'
+ self.mode = u'fortran'
else:
- raise ValueError("Invalid mode, expected 'c' or 'fortran', got %s" % mode)
+ raise ValueError, f"Invalid mode, expected 'c' or 'fortran', got {mode}"
- self.len = fill_contig_strides_array(self._shape, self._strides,
- itemsize, self.ndim, order)
+ self.len = fill_contig_strides_array(self._shape, self._strides, itemsize, self.ndim, order)
self.free_data = allocate_buffer
self.dtype_is_object = format == b'O'
- if allocate_buffer:
- # use malloc() for backwards compatibility
- # in case external code wants to change the data pointer
- self.data = malloc(self.len)
- if not self.data:
- raise MemoryError("unable to allocate array data.")
- if self.dtype_is_object:
- p = self.data
- for i in range(self.len / itemsize):
- p[i] = Py_None
- Py_INCREF(Py_None)
+ if allocate_buffer:
+ _allocate_buffer(self)
@cname('getbuffer')
def __getbuffer__(self, Py_buffer *info, int flags):
cdef int bufmode = -1
- if self.mode == u"c":
- bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
- elif self.mode == u"fortran":
- bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
- if not (flags & bufmode):
- raise ValueError("Can only create a buffer that is contiguous in memory.")
+ if flags & (PyBUF_C_CONTIGUOUS | PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS):
+ if self.mode == u"c":
+ bufmode = PyBUF_C_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
+ elif self.mode == u"fortran":
+ bufmode = PyBUF_F_CONTIGUOUS | PyBUF_ANY_CONTIGUOUS
+ if not (flags & bufmode):
+ raise ValueError, "Can only create a buffer that is contiguous in memory."
info.buf = self.data
info.len = self.len
- info.ndim = self.ndim
- info.shape = self._shape
- info.strides = self._strides
- info.suboffsets = NULL
- info.itemsize = self.itemsize
- info.readonly = 0
- if flags & PyBUF_FORMAT:
- info.format = self.format
+ if flags & PyBUF_STRIDES:
+ info.ndim = self.ndim
+ info.shape = self._shape
+ info.strides = self._strides
else:
- info.format = NULL
+ info.ndim = 1
+ info.shape = &self.len if flags & PyBUF_ND else NULL
+ info.strides = NULL
+ info.suboffsets = NULL
+ info.itemsize = self.itemsize
+ info.readonly = 0
+ info.format = self.format if flags & PyBUF_FORMAT else NULL
info.obj = self
- __pyx_getbuffer = capsule( &__pyx_array_getbuffer, "getbuffer(obj, view, flags)")
-
def __dealloc__(array self):
if self.callback_free_data != NULL:
self.callback_free_data(self.data)
- elif self.free_data:
+ elif self.free_data and self.data is not NULL:
if self.dtype_is_object:
- refcount_objects_in_slice(self.data, self._shape,
- self._strides, self.ndim, False)
+ refcount_objects_in_slice(self.data, self._shape, self._strides, self.ndim, inc=False)
free(self.data)
PyObject_Free(self._shape)
@@ -225,6 +216,8 @@
flags = PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT|PyBUF_WRITABLE
return memoryview(self, flags, self.dtype_is_object)
+ def __len__(self):
+ return self._shape[0]
def __getattr__(self, attr):
return getattr(self.memview, attr)
@@ -236,16 +229,35 @@
self.memview[item] = value
+@cname("__pyx_array_allocate_buffer")
+cdef int _allocate_buffer(array self) except -1:
+ # use malloc() for backwards compatibility
+ # in case external code wants to change the data pointer
+ cdef Py_ssize_t i
+ cdef PyObject **p
+
+ self.free_data = True
+ self.data = malloc(self.len)
+ if not self.data:
+ raise MemoryError, "unable to allocate array data."
+
+ if self.dtype_is_object:
+ p = self.data
+ for i in range(self.len // self.itemsize):
+ p[i] = Py_None
+ Py_INCREF(Py_None)
+ return 0
+
+
@cname("__pyx_array_new")
-cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format,
- char *mode, char *buf):
+cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char *c_mode, char *buf):
cdef array result
+ cdef str mode = "fortran" if c_mode[0] == b'f' else "c" # this often comes from a constant C string.
- if buf == NULL:
- result = array(shape, itemsize, format, mode.decode('ASCII'))
+ if buf is NULL:
+ result = array.__new__(array, shape, itemsize, format, mode)
else:
- result = array(shape, itemsize, format, mode.decode('ASCII'),
- allocate_buffer=False)
+ result = array.__new__(array, shape, itemsize, format, mode, allocate_buffer=False)
result.data = buf
return result
@@ -323,7 +335,7 @@
@cname('__pyx_memoryview')
-cdef class memoryview(object):
+cdef class memoryview:
cdef object obj
cdef object _size
@@ -368,6 +380,10 @@
def __dealloc__(memoryview self):
if self.obj is not None:
__Pyx_ReleaseBuffer(&self.view)
+ elif (<__pyx_buffer *> &self.view).obj == Py_None:
+ # Undo the incref in __cinit__() above.
+ (<__pyx_buffer *> &self.view).obj = NULL
+ Py_DECREF(Py_None)
cdef int i
global __pyx_memoryview_thread_locks_used
@@ -406,6 +422,9 @@
return self.convert_item_to_object(itemp)
def __setitem__(memoryview self, object index, object value):
+ if self.view.readonly:
+ raise TypeError, "Cannot assign to read-only memoryview"
+
have_slices, index = _unellipsify(index, self.view.ndim)
if have_slices:
@@ -420,7 +439,7 @@
cdef is_slice(self, obj):
if not isinstance(obj, memoryview):
try:
- obj = memoryview(obj, self.flags|PyBUF_ANY_CONTIGUOUS,
+ obj = memoryview(obj, self.flags & ~PyBUF_WRITABLE | PyBUF_ANY_CONTIGUOUS,
self.dtype_is_object)
except TypeError:
return None
@@ -430,10 +449,10 @@
cdef setitem_slice_assignment(self, dst, src):
cdef {{memviewslice_name}} dst_slice
cdef {{memviewslice_name}} src_slice
+ cdef {{memviewslice_name}} msrc = get_slice_from_memview(src, &src_slice)[0]
+ cdef {{memviewslice_name}} mdst = get_slice_from_memview(dst, &dst_slice)[0]
- memoryview_copy_contents(get_slice_from_memview(src, &src_slice)[0],
- get_slice_from_memview(dst, &dst_slice)[0],
- src.ndim, dst.ndim, self.dtype_is_object)
+ memoryview_copy_contents(msrc, mdst, src.ndim, dst.ndim, self.dtype_is_object)
cdef setitem_slice_assign_scalar(self, memoryview dst, value):
cdef int array[128]
@@ -481,7 +500,7 @@
try:
result = struct.unpack(self.view.format, bytesitem)
except struct.error:
- raise ValueError("Unable to convert item to object")
+ raise ValueError, "Unable to convert item to object"
else:
if len(self.view.format) == 1:
return result[0]
@@ -505,7 +524,10 @@
@cname('getbuffer')
def __getbuffer__(self, Py_buffer *info, int flags):
- if flags & PyBUF_STRIDES:
+ if flags & PyBUF_WRITABLE and self.view.readonly:
+ raise ValueError, "Cannot create writable memory view from read-only memoryview"
+
+ if flags & PyBUF_ND:
info.shape = self.view.shape
else:
info.shape = NULL
@@ -529,12 +551,10 @@
info.ndim = self.view.ndim
info.itemsize = self.view.itemsize
info.len = self.view.len
- info.readonly = 0
+ info.readonly = self.view.readonly
info.obj = self
- __pyx_getbuffer = capsule( &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")
-
- # Some properties that have the same sematics as in NumPy
+ # Some properties that have the same semantics as in NumPy
@property
def T(self):
cdef _memoryviewslice result = memoryview_copy(self)
@@ -543,6 +563,9 @@
@property
def base(self):
+ return self._get_base()
+
+ cdef _get_base(self):
return self.obj
@property
@@ -553,7 +576,7 @@
def strides(self):
if self.view.strides == NULL:
# Note: we always ask for strides, so if this is not set it's a bug
- raise ValueError("Buffer view does not expose strides")
+ raise ValueError, "Buffer view does not expose strides"
return tuple([stride for stride in self.view.strides[:self.view.ndim]])
@@ -654,39 +677,34 @@
Replace all ellipses with full slices and fill incomplete indices with
full slices.
"""
- if not isinstance(index, tuple):
- tup = (index,)
- else:
- tup = index
+ cdef Py_ssize_t idx
+ tup = index if isinstance(index, tuple) else (index,)
- result = []
+ result = [slice(None)] * ndim
have_slices = False
seen_ellipsis = False
- for idx, item in enumerate(tup):
+ idx = 0
+ for item in tup:
if item is Ellipsis:
if not seen_ellipsis:
- result.extend([slice(None)] * (ndim - len(tup) + 1))
+ idx += ndim - len(tup)
seen_ellipsis = True
- else:
- result.append(slice(None))
have_slices = True
else:
- if not isinstance(item, slice) and not PyIndex_Check(item):
- raise TypeError("Cannot index with type '%s'" % type(item))
-
- have_slices = have_slices or isinstance(item, slice)
- result.append(item)
-
- nslices = ndim - len(result)
- if nslices:
- result.extend([slice(None)] * nslices)
+ if isinstance(item, slice):
+ have_slices = True
+ elif not PyIndex_Check(item):
+ raise TypeError, f"Cannot index with type '{type(item)}'"
+ result[idx] = item
+ idx += 1
+ nslices = ndim - idx
return have_slices or nslices, tuple(result)
cdef assert_direct_dimensions(Py_ssize_t *suboffsets, int ndim):
for suboffset in suboffsets[:ndim]:
if suboffset >= 0:
- raise ValueError("Indirect dimensions not supported")
+ raise ValueError, "Indirect dimensions not supported"
#
### Slicing a memoryview
@@ -726,15 +744,16 @@
# may not be as expected"
cdef {{memviewslice_name}} *p_dst = &dst
cdef int *p_suboffset_dim = &suboffset_dim
- cdef Py_ssize_t start, stop, step
+ cdef Py_ssize_t start, stop, step, cindex
cdef bint have_start, have_stop, have_step
for dim, index in enumerate(indices):
if PyIndex_Check(index):
+ cindex = index
slice_memviewslice(
p_dst, p_src.shape[dim], p_src.strides[dim], p_src.suboffsets[dim],
dim, new_ndim, p_suboffset_dim,
- index, 0, 0, # start, stop, step
+ cindex, 0, 0, # start, stop, step
0, 0, 0, # have_{start,stop,step}
False)
elif index is None:
@@ -815,13 +834,16 @@
if start < 0:
start += shape
if not 0 <= start < shape:
- _err_dim(IndexError, "Index out of bounds (axis %d)", dim)
+ _err_dim(PyExc_IndexError, "Index out of bounds (axis %d)", dim)
else:
# index is a slice
- negative_step = have_step != 0 and step < 0
-
- if have_step and step == 0:
- _err_dim(ValueError, "Step may not be zero (axis %d)", dim)
+ if have_step:
+ negative_step = step < 0
+ if step == 0:
+ _err_dim(PyExc_ValueError, "Step may not be zero (axis %d)", dim)
+ else:
+ negative_step = False
+ step = 1
# check our bounds and set defaults
if have_start:
@@ -853,9 +875,6 @@
else:
stop = shape
- if not have_step:
- step = 1
-
# len = ceil( (stop - start) / step )
with cython.cdivision(True):
new_shape = (stop - start) // step
@@ -871,7 +890,7 @@
dst.shape[new_ndim] = new_shape
dst.suboffsets[new_ndim] = suboffset
- # Add the slicing or idexing offsets to the right suboffset or base data *
+ # Add the slicing or indexing offsets to the right suboffset or base data *
if suboffset_dim[0] < 0:
dst.data += start * stride
else:
@@ -882,7 +901,7 @@
if new_ndim == 0:
dst.data = ( dst.data)[0] + suboffset
else:
- _err_dim(IndexError, "All dimensions preceding dimension %d "
+ _err_dim(PyExc_IndexError, "All dimensions preceding dimension %d "
"must be indexed and not sliced", dim)
else:
suboffset_dim[0] = new_ndim
@@ -900,7 +919,7 @@
cdef char *resultp
if view.ndim == 0:
- shape = view.len / itemsize
+ shape = view.len // itemsize
stride = itemsize
else:
shape = view.shape[dim]
@@ -911,10 +930,10 @@
if index < 0:
index += view.shape[dim]
if index < 0:
- raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
+ raise IndexError, f"Out of bounds on buffer access (axis {dim})"
if index >= shape:
- raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
+ raise IndexError, f"Out of bounds on buffer access (axis {dim})"
resultp = bufp + index * stride
if suboffset >= 0:
@@ -926,7 +945,7 @@
### Transposing a memoryviewslice
#
@cname('__pyx_memslice_transpose')
-cdef int transpose_memslice({{memviewslice_name}} *memslice) nogil except 0:
+cdef int transpose_memslice({{memviewslice_name}} *memslice) nogil except -1:
cdef int ndim = memslice.memview.view.ndim
cdef Py_ssize_t *shape = memslice.shape
@@ -934,15 +953,15 @@
# reverse strides and shape
cdef int i, j
- for i in range(ndim / 2):
+ for i in range(ndim // 2):
j = ndim - 1 - i
strides[i], strides[j] = strides[j], strides[i]
shape[i], shape[j] = shape[j], shape[i]
if memslice.suboffsets[i] >= 0 or memslice.suboffsets[j] >= 0:
- _err(ValueError, "Cannot transpose memoryview with indirect dimensions")
+ _err(PyExc_ValueError, "Cannot transpose memoryview with indirect dimensions")
- return 1
+ return 0
#
### Creating new memoryview objects from slices and memoryviews
@@ -960,7 +979,7 @@
cdef int (*to_dtype_func)(char *, object) except 0
def __dealloc__(self):
- __PYX_XDEC_MEMVIEW(&self.from_slice, 1)
+ __PYX_XCLEAR_MEMVIEW(&self.from_slice, 1)
cdef convert_item_to_object(self, char *itemp):
if self.to_object_func != NULL:
@@ -974,12 +993,9 @@
else:
memoryview.assign_item_from_object(self, itemp, value)
- @property
- def base(self):
+ cdef _get_base(self):
return self.from_object
- __pyx_getbuffer = capsule( &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")
-
@cname('__pyx_memoryview_fromslice')
cdef memoryview_fromslice({{memviewslice_name}} memviewslice,
@@ -996,12 +1012,12 @@
# assert 0 < ndim <= memviewslice.memview.view.ndim, (
# ndim, memviewslice.memview.view.ndim)
- result = _memoryviewslice(None, 0, dtype_is_object)
+ result = _memoryviewslice.__new__(_memoryviewslice, None, 0, dtype_is_object)
result.from_slice = memviewslice
__PYX_INC_MEMVIEW(&memviewslice, 1)
- result.from_object = ( memviewslice.memview).base
+ result.from_object = ( memviewslice.memview)._get_base()
result.typeinfo = memviewslice.memview.typeinfo
result.view = memviewslice.memview.view
@@ -1010,7 +1026,10 @@
(<__pyx_buffer *> &result.view).obj = Py_None
Py_INCREF(Py_None)
- result.flags = PyBUF_RECORDS
+ if (memviewslice.memview).flags & PyBUF_WRITABLE:
+ result.flags = PyBUF_RECORDS
+ else:
+ result.flags = PyBUF_RECORDS_RO
result.view.shape = result.from_slice.shape
result.view.strides = result.from_slice.strides
@@ -1033,7 +1052,7 @@
@cname('__pyx_memoryview_get_slice_from_memoryview')
cdef {{memviewslice_name}} *get_slice_from_memview(memoryview memview,
- {{memviewslice_name}} *mslice):
+ {{memviewslice_name}} *mslice) except NULL:
cdef _memoryviewslice obj
if isinstance(memview, _memoryviewslice):
obj = memview
@@ -1090,10 +1109,7 @@
### Copy the contents of a memoryview slices
#
cdef Py_ssize_t abs_py_ssize_t(Py_ssize_t arg) nogil:
- if arg < 0:
- return -arg
- else:
- return arg
+ return -arg if arg < 0 else arg
@cname('__pyx_get_best_slice_order')
cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) nogil:
@@ -1159,11 +1175,10 @@
@cname('__pyx_memoryview_slice_get_size')
cdef Py_ssize_t slice_get_size({{memviewslice_name}} *src, int ndim) nogil:
"Return the size of the memory occupied by the slice in number of bytes"
- cdef int i
- cdef Py_ssize_t size = src.memview.view.itemsize
+ cdef Py_ssize_t shape, size = src.memview.view.itemsize
- for i in range(ndim):
- size *= src.shape[i]
+ for shape in src.shape[:ndim]:
+ size *= shape
return size
@@ -1180,11 +1195,11 @@
if order == 'F':
for idx in range(ndim):
strides[idx] = stride
- stride = stride * shape[idx]
+ stride *= shape[idx]
else:
for idx in range(ndim - 1, -1, -1):
strides[idx] = stride
- stride = stride * shape[idx]
+ stride *= shape[idx]
return stride
@@ -1205,7 +1220,7 @@
result = malloc(size)
if not result:
- _err(MemoryError, NULL)
+ _err_no_memory()
# tmpslice[0] = src
tmpslice.data = result
@@ -1214,8 +1229,7 @@
tmpslice.shape[i] = src.shape[i]
tmpslice.suboffsets[i] = -1
- fill_contig_strides_array(&tmpslice.shape[0], &tmpslice.strides[0], itemsize,
- ndim, order)
+ fill_contig_strides_array(&tmpslice.shape[0], &tmpslice.strides[0], itemsize, ndim, order)
# We need to broadcast strides again
for i in range(ndim):
@@ -1234,19 +1248,20 @@
@cname('__pyx_memoryview_err_extents')
cdef int _err_extents(int i, Py_ssize_t extent1,
Py_ssize_t extent2) except -1 with gil:
- raise ValueError("got differing extents in dimension %d (got %d and %d)" %
- (i, extent1, extent2))
+ raise ValueError, f"got differing extents in dimension {i} (got {extent1} and {extent2})"
@cname('__pyx_memoryview_err_dim')
-cdef int _err_dim(object error, char *msg, int dim) except -1 with gil:
- raise error(msg.decode('ascii') % dim)
+cdef int _err_dim(PyObject *error, str msg, int dim) except -1 with gil:
+ raise error, msg % dim
@cname('__pyx_memoryview_err')
-cdef int _err(object error, char *msg) except -1 with gil:
- if msg != NULL:
- raise error(msg.decode('ascii'))
- else:
- raise error
+cdef int _err(PyObject *error, str msg) except -1 with gil:
+ raise error, msg
+
+@cname('__pyx_memoryview_err_no_memory')
+cdef int _err_no_memory() except -1 with gil:
+ raise MemoryError
+
@cname('__pyx_memoryview_copy_contents')
cdef int memoryview_copy_contents({{memviewslice_name}} src,
@@ -1281,7 +1296,7 @@
_err_extents(i, dst.shape[i], src.shape[i])
if src.suboffsets[i] >= 0:
- _err_dim(ValueError, "Dimension %d is not direct", i)
+ _err_dim(PyExc_ValueError, "Dimension %d is not direct", i)
if slices_overlap(&src, &dst, ndim, itemsize):
# slices overlap, copy to temp, copy temp to dst
@@ -1301,9 +1316,9 @@
if direct_copy:
# Contiguous slices with same order
- refcount_copying(&dst, dtype_is_object, ndim, False)
+ refcount_copying(&dst, dtype_is_object, ndim, inc=False)
memcpy(dst.data, src.data, slice_get_size(&src, ndim))
- refcount_copying(&dst, dtype_is_object, ndim, True)
+ refcount_copying(&dst, dtype_is_object, ndim, inc=True)
free(tmpdata)
return 0
@@ -1313,9 +1328,9 @@
transpose_memslice(&src)
transpose_memslice(&dst)
- refcount_copying(&dst, dtype_is_object, ndim, False)
+ refcount_copying(&dst, dtype_is_object, ndim, inc=False)
copy_strided_to_strided(&src, &dst, ndim, itemsize)
- refcount_copying(&dst, dtype_is_object, ndim, True)
+ refcount_copying(&dst, dtype_is_object, ndim, inc=True)
free(tmpdata)
return 0
@@ -1338,18 +1353,15 @@
mslice.suboffsets[i] = -1
#
-### Take care of refcounting the objects in slices. Do this seperately from any copying,
+### Take care of refcounting the objects in slices. Do this separately from any copying,
### to minimize acquiring the GIL
#
@cname('__pyx_memoryview_refcount_copying')
-cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object,
- int ndim, bint inc) nogil:
- # incref or decref the objects in the destination slice if the dtype is
- # object
+cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object, int ndim, bint inc) nogil:
+ # incref or decref the objects in the destination slice if the dtype is object
if dtype_is_object:
- refcount_objects_in_slice_with_gil(dst.data, dst.shape,
- dst.strides, ndim, inc)
+ refcount_objects_in_slice_with_gil(dst.data, dst.shape, dst.strides, ndim, inc)
@cname('__pyx_memoryview_refcount_objects_in_slice_with_gil')
cdef void refcount_objects_in_slice_with_gil(char *data, Py_ssize_t *shape,
@@ -1361,6 +1373,7 @@
cdef void refcount_objects_in_slice(char *data, Py_ssize_t *shape,
Py_ssize_t *strides, int ndim, bint inc):
cdef Py_ssize_t i
+ cdef Py_ssize_t stride = strides[0]
for i in range(shape[0]):
if ndim == 1:
@@ -1369,10 +1382,9 @@
else:
Py_DECREF(( data)[0])
else:
- refcount_objects_in_slice(data, shape + 1, strides + 1,
- ndim - 1, inc)
+ refcount_objects_in_slice(data, shape + 1, strides + 1, ndim - 1, inc)
- data += strides[0]
+ data += stride
#
### Scalar to slice assignment
@@ -1381,10 +1393,9 @@
cdef void slice_assign_scalar({{memviewslice_name}} *dst, int ndim,
size_t itemsize, void *item,
bint dtype_is_object) nogil:
- refcount_copying(dst, dtype_is_object, ndim, False)
- _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim,
- itemsize, item)
- refcount_copying(dst, dtype_is_object, ndim, True)
+ refcount_copying(dst, dtype_is_object, ndim, inc=False)
+ _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim, itemsize, item)
+ refcount_copying(dst, dtype_is_object, ndim, inc=True)
@cname('__pyx_memoryview__slice_assign_scalar')
@@ -1401,8 +1412,7 @@
data += stride
else:
for i in range(extent):
- _slice_assign_scalar(data, shape + 1, strides + 1,
- ndim - 1, itemsize, item)
+ _slice_assign_scalar(data, shape + 1, strides + 1, ndim - 1, itemsize, item)
data += stride
@@ -1448,9 +1458,11 @@
cdef __Pyx_StructField *field
cdef __pyx_typeinfo_string fmt
cdef bytes part, result
+ cdef Py_ssize_t i
if type.typegroup == 'S':
- assert type.fields != NULL and type.fields.type != NULL
+ assert type.fields != NULL
+ assert type.fields.type != NULL
if type.flags & __PYX_BUF_FLAGS_PACKED_STRUCT:
alignment = b'^'
@@ -1468,10 +1480,9 @@
result = alignment.join(parts) + b'}'
else:
fmt = __Pyx_TypeInfoToFormat(type)
+ result = fmt.string
if type.arraysize[0]:
- extents = [unicode(type.arraysize[i]) for i in range(type.ndim)]
- result = (u"(%s)" % u','.join(extents)).encode('ascii') + fmt.string
- else:
- result = fmt.string
+ extents = [f"{type.arraysize[i]}" for i in range(type.ndim)]
+ result = f"({u','.join(extents)})".encode('ascii') + result
return result
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/ModuleSetupCode.c cython-0.20.1+1~202203241016-9537/Cython/Utility/ModuleSetupCode.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/ModuleSetupCode.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/ModuleSetupCode.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,3 +1,16 @@
+/////////////// InitLimitedAPI ///////////////
+
+#if defined(CYTHON_LIMITED_API) && 0 /* disabled: enabling Py_LIMITED_API needs more work */
+ #ifndef Py_LIMITED_API
+ #if CYTHON_LIMITED_API+0 > 0x03030000
+ #define Py_LIMITED_API CYTHON_LIMITED_API
+ #else
+ #define Py_LIMITED_API 0x03030000
+ #endif
+ #endif
+#endif
+
+
/////////////// CModulePreamble ///////////////
#include /* For offsetof */
@@ -5,7 +18,7 @@
#define offsetof(type, member) ( (size_t) & ((type*)0) -> member )
#endif
-#if !defined(WIN32) && !defined(MS_WINDOWS)
+#if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS)
#ifndef __stdcall
#define __stdcall
#endif
@@ -24,11 +37,12 @@
#define DL_EXPORT(t) t
#endif
+// For use in DL_IMPORT/DL_EXPORT macros.
+#define __PYX_COMMA ,
+
#ifndef HAVE_LONG_LONG
// CPython has required PY_LONG_LONG support for years, even if HAVE_LONG_LONG is not defined for us
- #if PY_VERSION_HEX >= 0x03030000 || (PY_MAJOR_VERSION == 2 && PY_VERSION_HEX >= 0x02070000)
- #define HAVE_LONG_LONG
- #endif
+ #define HAVE_LONG_LONG
#endif
#ifndef PY_LONG_LONG
@@ -39,15 +53,27 @@
#define Py_HUGE_VAL HUGE_VAL
#endif
-#ifdef PYPY_VERSION
- #define CYTHON_COMPILING_IN_PYPY 1
- #define CYTHON_COMPILING_IN_PYSTON 0
+#if defined(GRAALVM_PYTHON)
+ /* For very preliminary testing purposes. Most variables are set the same as PyPy.
+ The existence of this section does not imply that anything works or is even tested */
+ // GRAALVM_PYTHON test comes before PyPy test because GraalPython unhelpfully defines PYPY_VERSION
+ #define CYTHON_COMPILING_IN_PYPY 0
#define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 1
#undef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 0
- #undef CYTHON_USE_ASYNC_SLOTS
- #define CYTHON_USE_ASYNC_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #if PY_VERSION_HEX < 0x03050000
+ #undef CYTHON_USE_ASYNC_SLOTS
+ #define CYTHON_USE_ASYNC_SLOTS 0
+ #elif !defined(CYTHON_USE_ASYNC_SLOTS)
+ #define CYTHON_USE_ASYNC_SLOTS 1
+ #endif
#undef CYTHON_USE_PYLIST_INTERNALS
#define CYTHON_USE_PYLIST_INTERNALS 0
#undef CYTHON_USE_UNICODE_INTERNALS
@@ -64,60 +90,157 @@
#define CYTHON_UNPACK_METHODS 0
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
#undef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3)
+ #endif
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+#elif defined(PYPY_VERSION)
+ #define CYTHON_COMPILING_IN_PYPY 1
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
-#elif defined(PYSTON_VERSION)
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #if PY_VERSION_HEX < 0x03050000
+ #undef CYTHON_USE_ASYNC_SLOTS
+ #define CYTHON_USE_ASYNC_SLOTS 0
+ #elif !defined(CYTHON_USE_ASYNC_SLOTS)
+ #define CYTHON_USE_ASYNC_SLOTS 1
+ #endif
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #undef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 1
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3)
+ #endif
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+#elif defined(CYTHON_LIMITED_API)
+ // EXPERIMENTAL !!
#define CYTHON_COMPILING_IN_PYPY 0
- #define CYTHON_COMPILING_IN_PYSTON 1
#define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 1
+ #define CYTHON_COMPILING_IN_GRAAL 0
- #ifndef CYTHON_USE_TYPE_SLOTS
- #define CYTHON_USE_TYPE_SLOTS 1
- #endif
+ // CYTHON_CLINE_IN_TRACEBACK is currently disabled for the Limited API
+ #undef CYTHON_CLINE_IN_TRACEBACK
+ #define CYTHON_CLINE_IN_TRACEBACK 0
+
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 1
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
#undef CYTHON_USE_ASYNC_SLOTS
#define CYTHON_USE_ASYNC_SLOTS 0
#undef CYTHON_USE_PYLIST_INTERNALS
#define CYTHON_USE_PYLIST_INTERNALS 0
- #ifndef CYTHON_USE_UNICODE_INTERNALS
- #define CYTHON_USE_UNICODE_INTERNALS 1
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #ifndef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
#endif
- #undef CYTHON_USE_UNICODE_WRITER
- #define CYTHON_USE_UNICODE_WRITER 0
#undef CYTHON_USE_PYLONG_INTERNALS
#define CYTHON_USE_PYLONG_INTERNALS 0
#ifndef CYTHON_AVOID_BORROWED_REFS
#define CYTHON_AVOID_BORROWED_REFS 0
#endif
- #ifndef CYTHON_ASSUME_SAFE_MACROS
- #define CYTHON_ASSUME_SAFE_MACROS 1
- #endif
- #ifndef CYTHON_UNPACK_METHODS
- #define CYTHON_UNPACK_METHODS 1
- #endif
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
#undef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
#undef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 0
-
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 1
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 1
+ #endif
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
#else
#define CYTHON_COMPILING_IN_PYPY 0
- #define CYTHON_COMPILING_IN_PYSTON 0
#define CYTHON_COMPILING_IN_CPYTHON 1
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
#ifndef CYTHON_USE_TYPE_SLOTS
#define CYTHON_USE_TYPE_SLOTS 1
#endif
+ #ifndef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #endif
+ #ifndef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 1
+ #endif
#if PY_MAJOR_VERSION < 3
#undef CYTHON_USE_ASYNC_SLOTS
#define CYTHON_USE_ASYNC_SLOTS 0
#elif !defined(CYTHON_USE_ASYNC_SLOTS)
#define CYTHON_USE_ASYNC_SLOTS 1
#endif
- #if PY_VERSION_HEX < 0x02070000
- #undef CYTHON_USE_PYLONG_INTERNALS
- #define CYTHON_USE_PYLONG_INTERNALS 0
- #elif !defined(CYTHON_USE_PYLONG_INTERNALS)
+ #ifndef CYTHON_USE_PYLONG_INTERNALS
#define CYTHON_USE_PYLONG_INTERNALS 1
#endif
#ifndef CYTHON_USE_PYLIST_INTERNALS
@@ -126,7 +249,9 @@
#ifndef CYTHON_USE_UNICODE_INTERNALS
#define CYTHON_USE_UNICODE_INTERNALS 1
#endif
- #if PY_VERSION_HEX < 0x030300F0
+ #if PY_VERSION_HEX < 0x030300F0 || PY_VERSION_HEX >= 0x030B00A2
+ // Python 3.11a2 hid _PyLong_FormatAdvancedWriter and _PyFloat_FormatAdvancedWriter
+ // therefore disable unicode writer until a better alternative appears
#undef CYTHON_USE_UNICODE_WRITER
#define CYTHON_USE_UNICODE_WRITER 0
#elif !defined(CYTHON_USE_UNICODE_WRITER)
@@ -144,23 +269,252 @@
#ifndef CYTHON_FAST_THREAD_STATE
#define CYTHON_FAST_THREAD_STATE 1
#endif
+ #ifndef CYTHON_FAST_GIL
+ // Py3<3.5.2 does not support _PyThreadState_UncheckedGet().
+ #define CYTHON_FAST_GIL (PY_MAJOR_VERSION < 3 || PY_VERSION_HEX >= 0x03060000)
+ #endif
+ #ifndef CYTHON_METH_FASTCALL
+ // CPython 3.6 introduced METH_FASTCALL but with slightly different
+ // semantics. It became stable starting from CPython 3.7.
+ #define CYTHON_METH_FASTCALL (PY_VERSION_HEX >= 0x030700A1)
+ #endif
#ifndef CYTHON_FAST_PYCALL
#define CYTHON_FAST_PYCALL 1
#endif
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #if PY_VERSION_HEX < 0x03050000
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+ #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT)
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #ifndef CYTHON_USE_MODULE_STATE
+ // EXPERIMENTAL !!
+ #define CYTHON_USE_MODULE_STATE 0
+ #endif
+ #if PY_VERSION_HEX < 0x030400a1
+ #undef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #elif !defined(CYTHON_USE_TP_FINALIZE)
+ #define CYTHON_USE_TP_FINALIZE 1
+ #endif
+ #if PY_VERSION_HEX < 0x030600B1
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #elif !defined(CYTHON_USE_DICT_VERSIONS)
+ #define CYTHON_USE_DICT_VERSIONS 1
+ #endif
+ #if PY_VERSION_HEX < 0x030700A3
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+ #elif !defined(CYTHON_USE_EXC_INFO_STACK)
+ #define CYTHON_USE_EXC_INFO_STACK 1
+ #endif
#endif
#if !defined(CYTHON_FAST_PYCCALL)
#define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1)
#endif
+#if !defined(CYTHON_VECTORCALL)
+#define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL && PY_VERSION_HEX >= 0x030800B1)
+#endif
+
+/* Whether to use METH_FASTCALL with a fake backported implementation of vectorcall */
+#define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1)
+
#if CYTHON_USE_PYLONG_INTERNALS
- #include "longintrepr.h"
+ #if PY_MAJOR_VERSION < 3
+ #include "longintrepr.h"
+ #endif
/* These short defines can easily conflict with other code */
#undef SHIFT
#undef BASE
#undef MASK
+ /* Compile-time sanity check that these are indeed equal. Github issue #2670. */
+ #ifdef SIZEOF_VOID_P
+ enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
+ #endif
+#endif
+
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+
+#ifndef __has_cpp_attribute
+ #define __has_cpp_attribute(x) 0
+#endif
+
+// restrict
+#ifndef CYTHON_RESTRICT
+ #if defined(__GNUC__)
+ #define CYTHON_RESTRICT __restrict__
+ #elif defined(_MSC_VER) && _MSC_VER >= 1400
+ #define CYTHON_RESTRICT __restrict
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_RESTRICT restrict
+ #else
+ #define CYTHON_RESTRICT
+ #endif
+#endif
+
+// unused attribute
+#ifndef CYTHON_UNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+#endif
+
+#ifndef CYTHON_UNUSED_VAR
+# if defined(__cplusplus)
+ template void CYTHON_UNUSED_VAR( const T& ) { }
+# else
+# define CYTHON_UNUSED_VAR(x) (void)(x)
+# endif
+#endif
+
+#ifndef CYTHON_MAYBE_UNUSED_VAR
+ #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x)
+#endif
+
+#ifndef CYTHON_NCP_UNUSED
+# if CYTHON_COMPILING_IN_CPYTHON
+# define CYTHON_NCP_UNUSED
+# else
+# define CYTHON_NCP_UNUSED CYTHON_UNUSED
+# endif
+#endif
+
+#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None)
+
+#ifdef _MSC_VER
+ #ifndef _MSC_STDINT_H_
+ #if _MSC_VER < 1300
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+ #else
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+ #endif
+ #endif
+ #if _MSC_VER < 1300
+ #ifdef _WIN64
+ typedef unsigned long long __pyx_uintptr_t;
+ #else
+ typedef unsigned int __pyx_uintptr_t;
+ #endif
+ #else
+ #ifdef _WIN64
+ typedef unsigned __int64 __pyx_uintptr_t;
+ #else
+ typedef unsigned __int32 __pyx_uintptr_t;
+ #endif
+ #endif
+#else
+ #include
+ typedef uintptr_t __pyx_uintptr_t;
+#endif
+
+
+#ifndef CYTHON_FALLTHROUGH
+ #if defined(__cplusplus) && __cplusplus >= 201103L
+ #if __has_cpp_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH [[fallthrough]]
+ #elif __has_cpp_attribute(clang::fallthrough)
+ #define CYTHON_FALLTHROUGH [[clang::fallthrough]]
+ #elif __has_cpp_attribute(gnu::fallthrough)
+ #define CYTHON_FALLTHROUGH [[gnu::fallthrough]]
+ #endif
+ #endif
+
+ #ifndef CYTHON_FALLTHROUGH
+ #if __has_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH __attribute__((fallthrough))
+ #else
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+
+ #if defined(__clang__ ) && defined(__apple_build_version__)
+ #if __apple_build_version__ < 7000000 /* Xcode < 7.0 */
+ #undef CYTHON_FALLTHROUGH
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+#endif
+
+/////////////// CInitCode ///////////////
+
+// inline attribute
+#ifndef CYTHON_INLINE
+ #if defined(__clang__)
+ #define CYTHON_INLINE __inline__ __attribute__ ((__unused__))
+ #elif defined(__GNUC__)
+ #define CYTHON_INLINE __inline__
+ #elif defined(_MSC_VER)
+ #define CYTHON_INLINE __inline
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_INLINE inline
+ #else
+ #define CYTHON_INLINE
+ #endif
+#endif
+
+
+/////////////// CppInitCode ///////////////
+
+#ifndef __cplusplus
+ #error "Cython files generated with the C++ option must be compiled with a C++ compiler."
#endif
+// inline attribute
+#ifndef CYTHON_INLINE
+ #if defined(__clang__)
+ #define CYTHON_INLINE __inline__ __attribute__ ((__unused__))
+ #else
+ #define CYTHON_INLINE inline
+ #endif
+#endif
+
+// Work around clang bug https://stackoverflow.com/questions/21847816/c-invoke-nested-template-class-destructor
+template
+void __Pyx_call_destructor(T& x) {
+ x.~T();
+}
+
+// Used for temporary variables of "reference" type.
+template
+class __Pyx_FakeReference {
+ public:
+ __Pyx_FakeReference() : ptr(NULL) { }
+ // __Pyx_FakeReference(T& ref) : ptr(&ref) { }
+ // Const version needed as Cython doesn't know about const overloads (e.g. for stl containers).
+ __Pyx_FakeReference(const T& ref) : ptr(const_cast(&ref)) { }
+ T *operator->() { return ptr; }
+ T *operator&() { return ptr; }
+ operator T&() { return *ptr; }
+ // TODO(robertwb): Delegate all operators (or auto-generate unwrapping code where needed).
+ template bool operator ==(U other) { return *ptr == other; }
+ template bool operator !=(U other) { return *ptr != other; }
+ private:
+ T *ptr;
+};
+
+
+/////////////// PythonCompatibility ///////////////
+
#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag)
#define Py_OptimizeFlag 0
#endif
@@ -170,14 +524,96 @@
#if PY_MAJOR_VERSION < 3
#define __Pyx_BUILTIN_MODULE_NAME "__builtin__"
- #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
- PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#define __Pyx_DefaultClassType PyClass_Type
+ #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
+ PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#else
#define __Pyx_BUILTIN_MODULE_NAME "builtins"
- #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
- PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#define __Pyx_DefaultClassType PyType_Type
+#if PY_VERSION_HEX >= 0x030B00A1
+ static CYTHON_INLINE PyCodeObject* __Pyx_PyCode_New(int a, int p, int k, int l, int s, int f,
+ PyObject *code, PyObject *c, PyObject* n, PyObject *v,
+ PyObject *fv, PyObject *cell, PyObject* fn,
+ PyObject *name, int fline, PyObject *lnos) {
+ // TODO - currently written to be simple and work in limited API etc.
+ // A more optimized version would be good
+ PyObject *kwds=NULL, *argcount=NULL, *posonlyargcount=NULL, *kwonlyargcount=NULL;
+ PyObject *nlocals=NULL, *stacksize=NULL, *flags=NULL, *replace=NULL, *call_result=NULL, *empty=NULL;
+ const char *fn_cstr=NULL;
+ const char *name_cstr=NULL;
+ PyCodeObject* co=NULL;
+ PyObject *type, *value, *traceback;
+
+ // we must be able to call this while an exception is happening - thus clear then restore the state
+ PyErr_Fetch(&type, &value, &traceback);
+
+ if (!(kwds=PyDict_New())) goto end;
+ if (!(argcount=PyLong_FromLong(a))) goto end;
+ if (PyDict_SetItemString(kwds, "co_argcount", argcount) != 0) goto end;
+ if (!(posonlyargcount=PyLong_FromLong(p))) goto end;
+ if (PyDict_SetItemString(kwds, "co_posonlyargcount", posonlyargcount) != 0) goto end;
+ if (!(kwonlyargcount=PyLong_FromLong(k))) goto end;
+ if (PyDict_SetItemString(kwds, "co_kwonlyargcount", kwonlyargcount) != 0) goto end;
+ if (!(nlocals=PyLong_FromLong(l))) goto end;
+ if (PyDict_SetItemString(kwds, "co_nlocals", nlocals) != 0) goto end;
+ if (!(stacksize=PyLong_FromLong(s))) goto end;
+ if (PyDict_SetItemString(kwds, "co_stacksize", stacksize) != 0) goto end;
+ if (!(flags=PyLong_FromLong(f))) goto end;
+ if (PyDict_SetItemString(kwds, "co_flags", flags) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_code", code) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_consts", c) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_names", n) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_varnames", v) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_freevars", fv) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_cellvars", cell) != 0) goto end;
+ if (PyDict_SetItemString(kwds, "co_linetable", lnos) != 0) goto end;
+
+ if (!(fn_cstr=PyUnicode_AsUTF8AndSize(fn, NULL))) goto end;
+ if (!(name_cstr=PyUnicode_AsUTF8AndSize(name, NULL))) goto end;
+ if (!(co = PyCode_NewEmpty(fn_cstr, name_cstr, fline))) goto end;
+
+ if (!(replace = PyObject_GetAttrString((PyObject*)co, "replace"))) goto cleanup_code_too;
+ if (!(empty = PyTuple_New(0))) goto cleanup_code_too; // unfortunately __pyx_empty_tuple isn't available here
+ if (!(call_result = PyObject_Call(replace, empty, kwds))) goto cleanup_code_too;
+
+ Py_XDECREF((PyObject*)co);
+ co = (PyCodeObject*)call_result;
+ call_result = NULL;
+
+ if (0) {
+ cleanup_code_too:
+ Py_XDECREF((PyObject*)co);
+ co = NULL;
+ }
+ end:
+ Py_XDECREF(kwds);
+ Py_XDECREF(argcount);
+ Py_XDECREF(posonlyargcount);
+ Py_XDECREF(kwonlyargcount);
+ Py_XDECREF(nlocals);
+ Py_XDECREF(stacksize);
+ Py_XDECREF(replace);
+ Py_XDECREF(call_result);
+ Py_XDECREF(empty);
+ if (type) {
+ PyErr_Restore(type, value, traceback);
+ }
+ return co;
+ }
+#elif PY_VERSION_HEX >= 0x030800B2 && !CYTHON_COMPILING_IN_PYPY
+
+ #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
+ PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#else
+ #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
+ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#endif
+#endif
+
+#if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE)
+ #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type)
+#else
+ #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type))
#endif
#ifndef Py_TPFLAGS_CHECKTYPES
@@ -193,34 +629,297 @@
#define Py_TPFLAGS_HAVE_FINALIZE 0
#endif
-#ifndef METH_FASTCALL
- // new in CPython 3.6
- #define METH_FASTCALL 0x80
- typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject **args,
- Py_ssize_t nargs, PyObject *kwnames);
+#ifndef METH_STACKLESS
+ // already defined for Stackless Python (all versions) and C-Python >= 3.7
+ // value if defined: Stackless Python < 3.6: 0x80 else 0x100
+ #define METH_STACKLESS 0
+#endif
+#if PY_VERSION_HEX <= 0x030700A3 || !defined(METH_FASTCALL)
+ // new in CPython 3.6, but changed in 3.7 - see
+ // positional-only parameters:
+ // https://bugs.python.org/issue29464
+ // const args:
+ // https://bugs.python.org/issue32240
+ #ifndef METH_FASTCALL
+ #define METH_FASTCALL 0x80
+ #endif
+ typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs);
+ // new in CPython 3.7, used to be old signature of _PyCFunctionFast() in 3.6
+ typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args,
+ Py_ssize_t nargs, PyObject *kwnames);
#else
#define __Pyx_PyCFunctionFast _PyCFunctionFast
+ #define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords
+#endif
+
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_METH_FASTCALL METH_FASTCALL
+ #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast
+ #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords
+#else
+ #define __Pyx_METH_FASTCALL METH_VARARGS
+ #define __Pyx_PyCFunction_FastCall PyCFunction
+ #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords
+#endif
+
+#if CYTHON_VECTORCALL
+ #define __pyx_vectorcallfunc vectorcallfunc
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET
+ #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n))
+#elif CYTHON_BACKPORT_VECTORCALL
+ typedef PyObject *(*__pyx_vectorcallfunc)(PyObject *callable, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames);
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1))
+ #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(((size_t)(n)) & ~__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET))
+#else
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0
+ #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n))
+#endif
+
+// PEP-573: PyCFunction holds reference to defining class (PyCMethodObject)
+#if PY_VERSION_HEX < 0x030900B1
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b))
+ typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *);
+#else
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b)
+ #define __Pyx_PyCMethod PyCMethod
+#endif
+#ifndef METH_METHOD
+ #define METH_METHOD 0x200
+#endif
+
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc)
+ #define PyObject_Malloc(s) PyMem_Malloc(s)
+ #define PyObject_Free(p) PyMem_Free(p)
+ #define PyObject_Realloc(p) PyMem_Realloc(p)
+#endif
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno)
+#else
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno)
+#endif
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyThreadState_Current PyThreadState_Get()
+#elif !CYTHON_FAST_THREAD_STATE
+ #define __Pyx_PyThreadState_Current PyThreadState_GET()
+#elif PY_VERSION_HEX >= 0x03060000
+ //#elif PY_VERSION_HEX >= 0x03050200
+ // Actually added in 3.5.2, but compiling against that does not guarantee that we get imported there.
+ #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet()
+#elif PY_VERSION_HEX >= 0x03000000
+ #define __Pyx_PyThreadState_Current PyThreadState_GET()
+#else
+ #define __Pyx_PyThreadState_Current _PyThreadState_Current
+#endif
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE void *__Pyx_PyModule_GetState(PyObject *op)
+{
+ void *result;
+
+ result = PyModule_GetState(op);
+ if (!result)
+ Py_FatalError("Couldn't find the module state");
+ return result;
+}
#endif
-#if CYTHON_FAST_PYCCALL
-#define __Pyx_PyFastCFunction_Check(func) \
- ((PyCFunction_Check(func) && METH_FASTCALL == PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)))
+
+#define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE(obj), name, func_ctype)
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name))
#else
-#define __Pyx_PyFastCFunction_Check(func) 0
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name)
+#endif
+
+// TSS (Thread Specific Storage) API
+#if PY_VERSION_HEX < 0x030700A2 && !defined(PyThread_tss_create) && !defined(Py_tss_NEEDS_INIT)
+#include "pythread.h"
+#define Py_tss_NEEDS_INIT 0
+typedef int Py_tss_t;
+static CYTHON_INLINE int PyThread_tss_create(Py_tss_t *key) {
+ *key = PyThread_create_key();
+ return 0; /* PyThread_create_key reports success always */
+}
+static CYTHON_INLINE Py_tss_t * PyThread_tss_alloc(void) {
+ Py_tss_t *key = (Py_tss_t *)PyObject_Malloc(sizeof(Py_tss_t));
+ *key = Py_tss_NEEDS_INIT;
+ return key;
+}
+static CYTHON_INLINE void PyThread_tss_free(Py_tss_t *key) {
+ PyObject_Free(key);
+}
+static CYTHON_INLINE int PyThread_tss_is_created(Py_tss_t *key) {
+ return *key != Py_tss_NEEDS_INIT;
+}
+static CYTHON_INLINE void PyThread_tss_delete(Py_tss_t *key) {
+ PyThread_delete_key(*key);
+ *key = Py_tss_NEEDS_INIT;
+}
+static CYTHON_INLINE int PyThread_tss_set(Py_tss_t *key, void *value) {
+ return PyThread_set_key_value(*key, value);
+}
+static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
+ return PyThread_get_key_value(*key);
+}
+// PyThread_delete_key_value(key) is equivalent to PyThread_set_key_value(key, NULL)
+// PyThread_ReInitTLS() is a no-op
+#endif /* TSS (Thread Specific Storage) API */
+
+
+#if PY_MAJOR_VERSION < 3
+ #if CYTHON_COMPILING_IN_PYPY
+ #if PYPY_VERSION_NUM < 0x07030600
+ #if defined(__cplusplus) && __cplusplus >= 201402L
+ [[deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")]]
+ #elif defined(__GNUC__) || defined(__clang__)
+ __attribute__ ((__deprecated__("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")))
+ #elif defined(_MSC_VER)
+ __declspec(deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6"))
+ #endif
+ static CYTHON_INLINE int PyGILState_Check(void) {
+ // PyGILState_Check is used to decide whether to release the GIL when we don't
+ // know that we have it. For PyPy2 it isn't possible to check.
+ // Therefore assume that we don't have the GIL (which causes us not to release it,
+ // but is "safe")
+ return 0;
+ }
+ #else // PYPY_VERSION_NUM < 0x07030600
+ // PyPy2 >= 7.3.6 has PyGILState_Check
+ #endif // PYPY_VERSION_NUM < 0x07030600
+ #else
+ // https://stackoverflow.com/a/25666624
+ static CYTHON_INLINE int PyGILState_Check(void) {
+ PyThreadState * tstate = _PyThreadState_Current;
+ return tstate && (tstate == PyGILState_GetThisThreadState());
+ }
+ #endif
#endif
-/* new Py3.3 unicode type (PEP 393) */
-#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
+#if CYTHON_COMPILING_IN_CPYTHON || defined(_PyDict_NewPresized)
+#define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n))
+#else
+#define __Pyx_PyDict_NewPresized(n) PyDict_New()
+#endif
+
+#if PY_MAJOR_VERSION >= 3 || CYTHON_FUTURE_DIVISION
+ #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)
+ #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)
+#else
+ #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y)
+ #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y)
+#endif
+
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX > 0x030600B4 && CYTHON_USE_UNICODE_INTERNALS
+// _PyDict_GetItem_KnownHash() exists since CPython 3.5, but it was
+// dropping exceptions. Since 3.6, exceptions are kept.
+#define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash)
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) {
+ PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name);
+ if (res == NULL) PyErr_Clear();
+ return res;
+}
+#elif PY_MAJOR_VERSION >= 3 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000)
+#define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#else
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) {
+ // This is tricky - we should return a borrowed reference but not swallow non-KeyError exceptions. 8-|
+ // But: this function is only used in Py2 and older PyPys,
+ // and currently only for argument parsing and other non-correctness-critical lookups
+ // and we know that 'name' is an interned 'str' with pre-calculated hash value (only comparisons can fail),
+ // thus, performance matters more than correctness here, especially in the "not found" case.
+#if CYTHON_COMPILING_IN_PYPY
+ // So we ignore any exceptions in old PyPys ...
+ return PyDict_GetItem(dict, name);
+#else
+ // and hack together a stripped-down and modified PyDict_GetItem() in CPython 2.
+ PyDictEntry *ep;
+ PyDictObject *mp = (PyDictObject*) dict;
+ long hash = ((PyStringObject *) name)->ob_shash;
+ assert(hash != -1); /* hash values of interned strings are always initialised */
+ ep = (mp->ma_lookup)(mp, name, hash);
+ if (ep == NULL) {
+ // error occurred
+ return NULL;
+ }
+ // found or not found
+ return ep->me_value;
+#endif
+}
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#endif
+
+/* Type slots */
+
+#if CYTHON_USE_TYPE_SLOTS
+ #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags)
+ #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0)
+ #define __Pyx_PyObject_GetIterNextFunc(obj) (Py_TYPE(obj)->tp_iternext)
+#else
+ #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp))
+ #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature)
+ #define __Pyx_PyObject_GetIterNextFunc(obj) PyIter_Next
+#endif
+
+#if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000
+// In Py3.8+, instances of heap types need to decref their type on deallocation.
+// https://bugs.python.org/issue35810
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) { \
+ PyTypeObject *type = Py_TYPE(obj); \
+ assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)); \
+ PyObject_GC_Del(obj); \
+ Py_DECREF(type); \
+}
+#else
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj)
+#endif
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define CYTHON_PEP393_ENABLED 1
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GetLength(u)
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i)
+ #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111)
+ #define __Pyx_PyUnicode_KIND(u) ((void)u, (0))
+ // __Pyx_PyUnicode_DATA() and __Pyx_PyUnicode_READ() must go together, e.g. for iteration.
+ #define __Pyx_PyUnicode_DATA(u) ((void*)u)
+ #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i))
+ //#define __Pyx_PyUnicode_WRITE(k, d, i, ch) /* not available */
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u))
+#elif PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
+ /* new Py3.3 unicode type (PEP 393) */
#define CYTHON_PEP393_ENABLED 1
+
+ #if defined(PyUnicode_IS_READY)
#define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ? \
0 : _PyUnicode_Ready((PyObject *)(op)))
+ #else
+ // Py3.12 / PEP-623 will remove wstr type unicode strings and all of the PyUnicode_READY() machinery.
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #endif
+
#define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u)
#define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
#define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u)
- #define __Pyx_PyUnicode_KIND(u) PyUnicode_KIND(u)
+ #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u))
#define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u)
#define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
#define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, ch)
+ #if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE)
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
+ // Avoid calling deprecated C-API functions in Py3.9+ that PEP-623 schedules for removal in Py3.12.
+ // https://www.python.org/dev/peps/pep-0623/
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
+ #else
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
+ #endif
+ #else
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u))
+ #endif
#else
#define CYTHON_PEP393_ENABLED 0
#define PyUnicode_1BYTE_KIND 1
@@ -230,9 +929,9 @@
#define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u)
#define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i]))
#define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((sizeof(Py_UNICODE) == 2) ? 65535 : 1114111)
- #define __Pyx_PyUnicode_KIND(u) (sizeof(Py_UNICODE))
+ #define __Pyx_PyUnicode_KIND(u) ((int)sizeof(Py_UNICODE))
#define __Pyx_PyUnicode_DATA(u) ((void*)PyUnicode_AS_UNICODE(u))
- /* (void)(k) => avoid unused variable warning due to macro: */
+ // (void)(k) => avoid unused variable warning due to macro:
#define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i]))
#define __Pyx_PyUnicode_WRITE(k, d, i, ch) (((void)(k)), ((Py_UNICODE*)d)[i] = ch)
#define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_SIZE(u))
@@ -247,35 +946,25 @@
PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b))
#endif
-#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_Contains)
- #define PyUnicode_Contains(u, s) PySequence_Contains(u, s)
-#endif
-
-#if CYTHON_COMPILING_IN_PYPY && !defined(PyByteArray_Check)
- #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type)
-#endif
-
-#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Format)
- #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
-#endif
-
-#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc)
- #define PyObject_Malloc(s) PyMem_Malloc(s)
- #define PyObject_Free(p) PyMem_Free(p)
- #define PyObject_Realloc(p) PyMem_Realloc(p)
-#endif
-
-#if CYTHON_COMPILING_IN_PYSTON
- // special C-API functions only in Pyston
- #define __Pyx_PyCode_HasFreeVars(co) PyCode_HasFreeVars(co)
- #define __Pyx_PyFrame_SetLineNumber(frame, lineno) PyFrame_SetLineNumber(frame, lineno)
-#else
- #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
- #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno)
+#if CYTHON_COMPILING_IN_PYPY
+ #if !defined(PyUnicode_DecodeUnicodeEscape)
+ #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors)
+ #endif
+ #if !defined(PyUnicode_Contains) || (PY_MAJOR_VERSION == 2 && PYPY_VERSION_NUM < 0x07030500)
+ #undef PyUnicode_Contains
+ #define PyUnicode_Contains(u, s) PySequence_Contains(u, s)
+ #endif
+ #if !defined(PyByteArray_Check)
+ #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type)
+ #endif
+ #if !defined(PyObject_Format)
+ #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
+ #endif
#endif
-#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
-#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
+// ("..." % x) must call PyNumber_Remainder() if x is a string subclass that implements "__rmod__()".
+#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyString_Check(b) && !PyString_CheckExact(b)))) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
+#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
#if PY_MAJOR_VERSION >= 3
#define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b)
@@ -293,6 +982,10 @@
#define PyString_Type PyUnicode_Type
#define PyString_Check PyUnicode_Check
#define PyString_CheckExact PyUnicode_CheckExact
+ // PyPy3 used to define "PyObject_Unicode"
+#ifndef PyObject_Unicode
+ #define PyObject_Unicode PyObject_Str
+#endif
#endif
#if PY_MAJOR_VERSION >= 3
@@ -303,11 +996,31 @@
#define __Pyx_PyBaseString_CheckExact(obj) (PyString_CheckExact(obj) || PyUnicode_CheckExact(obj))
#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+ #define __Pyx_PySequence_ListKeepNew(obj) \
+ (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj))
+#else
+ #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj)
+#endif
+
#ifndef PySet_CheckExact
- #define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type)
+ #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type)
#endif
-#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type)
+#if PY_VERSION_HEX >= 0x030900A4
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size)
+#else
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size)
+#endif
+
+#if CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq)
+#else
+ // NOTE: might fail with exception => check for -1
+ #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq)
+#endif
#if PY_MAJOR_VERSION >= 3
#define PyIntObject PyLongObject
@@ -340,16 +1053,10 @@
#if PY_VERSION_HEX < 0x030200A4
typedef long Py_hash_t;
#define __Pyx_PyInt_FromHash_t PyInt_FromLong
- #define __Pyx_PyInt_AsHash_t PyInt_AsLong
+ #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsHash_t
#else
#define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t
- #define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t
-#endif
-
-#if PY_MAJOR_VERSION >= 3
- #define __Pyx_PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func))
-#else
- #define __Pyx_PyMethod_New(func, self, klass) PyMethod_New(func, self, klass)
+ #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsSsize_t
#endif
// backport of PyAsyncMethods from Py3.5 to older Py3.x versions
@@ -359,88 +1066,228 @@
#define __Pyx_PyAsyncMethodsStruct PyAsyncMethods
#define __Pyx_PyType_AsAsync(obj) (Py_TYPE(obj)->tp_as_async)
#else
+ #define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved))
+ #endif
+#else
+ #define __Pyx_PyType_AsAsync(obj) NULL
+#endif
+#ifndef __Pyx_PyAsyncMethodsStruct
typedef struct {
unaryfunc am_await;
unaryfunc am_aiter;
unaryfunc am_anext;
} __Pyx_PyAsyncMethodsStruct;
- #define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved))
- #endif
-#else
- #define __Pyx_PyType_AsAsync(obj) NULL
#endif
-// restrict
-#ifndef CYTHON_RESTRICT
- #if defined(__GNUC__)
- #define CYTHON_RESTRICT __restrict__
- #elif defined(_MSC_VER) && _MSC_VER >= 1400
- #define CYTHON_RESTRICT __restrict
- #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
- #define CYTHON_RESTRICT restrict
- #else
- #define CYTHON_RESTRICT
- #endif
+
+/////////////// IncludeStructmemberH.proto ///////////////
+
+#include
+
+
+/////////////// SmallCodeConfig.proto ///////////////
+
+#ifndef CYTHON_SMALL_CODE
+#if defined(__clang__)
+ #define CYTHON_SMALL_CODE
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define CYTHON_SMALL_CODE __attribute__((cold))
+#else
+ #define CYTHON_SMALL_CODE
+#endif
#endif
-#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None)
+/////////////// PyModInitFuncType.proto ///////////////
-/////////////// CInitCode ///////////////
+#ifndef CYTHON_NO_PYINIT_EXPORT
+#define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC
-// inline attribute
-#ifndef CYTHON_INLINE
- #if defined(__GNUC__)
- #define CYTHON_INLINE __inline__
- #elif defined(_MSC_VER)
- #define CYTHON_INLINE __inline
- #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
- #define CYTHON_INLINE inline
- #else
- #define CYTHON_INLINE
- #endif
+#elif PY_MAJOR_VERSION < 3
+// Py2: define this to void manually because PyMODINIT_FUNC adds __declspec(dllexport) to it's definition.
+#ifdef __cplusplus
+#define __Pyx_PyMODINIT_FUNC extern "C" void
+#else
+#define __Pyx_PyMODINIT_FUNC void
#endif
+#else
+// Py3+: define this to PyObject * manually because PyMODINIT_FUNC adds __declspec(dllexport) to it's definition.
+#ifdef __cplusplus
+#define __Pyx_PyMODINIT_FUNC extern "C" PyObject *
+#else
+#define __Pyx_PyMODINIT_FUNC PyObject *
+#endif
+#endif
-/////////////// CppInitCode ///////////////
-#ifndef __cplusplus
- #error "Cython files generated with the C++ option must be compiled with a C++ compiler."
+/////////////// FastTypeChecks.proto ///////////////
+
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2)
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b);/*proto*/
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b);/*proto*/
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type);/*proto*/
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2);/*proto*/
+#else
+#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2))
+#define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type)
+#define __Pyx_PyErr_GivenExceptionMatches2(err, type1, type2) (PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2))
+#endif
+#define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_Occurred(), err1, err2)
+
+#define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception)
+
+/////////////// FastTypeChecks ///////////////
+//@requires: Exceptions.c::PyThreadStateGet
+//@requires: Exceptions.c::PyErrFetchRestore
+
+#if CYTHON_COMPILING_IN_CPYTHON
+static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) {
+ while (a) {
+ a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*);
+ if (a == b)
+ return 1;
+ }
+ return b == &PyBaseObject_Type;
+}
+
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (a == b) return 1;
+ mro = a->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ // should only get here for incompletely initialised types, i.e. never under normal usage patterns
+ return __Pyx_InBases(a, b);
+}
+
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (cls == a || cls == b) return 1;
+ mro = cls->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ PyObject *base = PyTuple_GET_ITEM(mro, i);
+ if (base == (PyObject *)a || base == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ // should only get here for incompletely initialised types, i.e. never under normal usage patterns
+ return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b);
+}
+
+
+#if PY_MAJOR_VERSION == 2
+static int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject* exc_type2) {
+ // PyObject_IsSubclass() can recurse and therefore is not safe
+ PyObject *exception, *value, *tb;
+ int res;
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ __Pyx_ErrFetch(&exception, &value, &tb);
+
+ res = exc_type1 ? PyObject_IsSubclass(err, exc_type1) : 0;
+ // This function must not fail, so print the error here (which also clears it)
+ if (unlikely(res == -1)) {
+ PyErr_WriteUnraisable(err);
+ res = 0;
+ }
+ if (!res) {
+ res = PyObject_IsSubclass(err, exc_type2);
+ // This function must not fail, so print the error here (which also clears it)
+ if (unlikely(res == -1)) {
+ PyErr_WriteUnraisable(err);
+ res = 0;
+ }
+ }
+
+ __Pyx_ErrRestore(exception, value, tb);
+ return res;
+}
+#else
+static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) {
+ if (exc_type1) {
+ return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2);
+ } else {
+ return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2);
+ }
+}
#endif
-// inline attribute
-#ifndef CYTHON_INLINE
- #define CYTHON_INLINE inline
+// so far, we only call PyErr_GivenExceptionMatches() with an exception type (not instance) as first argument
+// => optimise for that case
+
+static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ assert(PyExceptionClass_Check(exc_type));
+ n = PyTuple_GET_SIZE(tuple);
+#if PY_MAJOR_VERSION >= 3
+ // the tighter subtype checking in Py3 allows faster out-of-order comparison
+ for (i=0; i
-void __Pyx_call_destructor(T& x) {
- x.~T();
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject* exc_type) {
+ if (likely(err == exc_type)) return 1;
+ if (likely(PyExceptionClass_Check(err))) {
+ if (likely(PyExceptionClass_Check(exc_type))) {
+ return __Pyx_inner_PyErr_GivenExceptionMatches2(err, NULL, exc_type);
+ } else if (likely(PyTuple_Check(exc_type))) {
+ return __Pyx_PyErr_GivenExceptionMatchesTuple(err, exc_type);
+ } else {
+ // FIXME: Py3: PyErr_SetString(PyExc_TypeError, "catching classes that do not inherit from BaseException is not allowed");
+ }
+ }
+ return PyErr_GivenExceptionMatches(err, exc_type);
}
-// Used for temporary variables of "reference" type.
-template
-class __Pyx_FakeReference {
- public:
- __Pyx_FakeReference() : ptr(NULL) { }
- // __Pyx_FakeReference(T& ref) : ptr(&ref) { }
- // Const version needed as Cython doesn't know about const overloads (e.g. for stl containers).
- __Pyx_FakeReference(const T& ref) : ptr(const_cast(&ref)) { }
- T *operator->() { return ptr; }
- operator T&() { return *ptr; }
- // TODO(robertwb): Delegate all operators (or auto-generate unwrapping code where needed).
- template bool operator ==(U other) { return *ptr == other; };
- template bool operator !=(U other) { return *ptr != other; };
- private:
- T *ptr;
-};
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *exc_type1, PyObject *exc_type2) {
+ // Only used internally with known exception types => pure safety check assertions.
+ assert(PyExceptionClass_Check(exc_type1));
+ assert(PyExceptionClass_Check(exc_type2));
+ if (likely(err == exc_type1 || err == exc_type2)) return 1;
+ if (likely(PyExceptionClass_Check(err))) {
+ return __Pyx_inner_PyErr_GivenExceptionMatches2(err, exc_type1, exc_type2);
+ }
+ return (PyErr_GivenExceptionMatches(err, exc_type1) || PyErr_GivenExceptionMatches(err, exc_type2));
+}
+
+#endif
/////////////// MathInitCode ///////////////
-#if defined(WIN32) || defined(MS_WINDOWS)
- #define _USE_MATH_DEFINES
+#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
+ #if !defined(_USE_MATH_DEFINES)
+ #define _USE_MATH_DEFINES
+ #endif
#endif
#include
@@ -466,33 +1313,11 @@
/////////////// UtilityFunctionPredeclarations.proto ///////////////
-/* unused attribute */
-#ifndef CYTHON_UNUSED
-# if defined(__GNUC__)
-# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
-# define CYTHON_UNUSED __attribute__ ((__unused__))
-# else
-# define CYTHON_UNUSED
-# endif
-# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER))
-# define CYTHON_UNUSED __attribute__ ((__unused__))
-# else
-# define CYTHON_UNUSED
-# endif
-#endif
-
-#ifndef CYTHON_NCP_UNUSED
-# if CYTHON_COMPILING_IN_CPYTHON
-# define CYTHON_NCP_UNUSED
-# else
-# define CYTHON_NCP_UNUSED CYTHON_UNUSED
-# endif
-#endif
-
typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* encoding;
const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/
/////////////// ForceInitThreads.proto ///////////////
+//@proto_block: utility_code_proto_before_types
#ifndef __PYX_FORCE_INIT_THREADS
#define __PYX_FORCE_INIT_THREADS 0
@@ -500,12 +1325,107 @@
/////////////// InitThreads.init ///////////////
-#ifdef WITH_THREAD
+#if defined(WITH_THREAD) && PY_VERSION_HEX < 0x030700F0
PyEval_InitThreads();
#endif
+
+/////////////// ModuleCreationPEP489 ///////////////
+//@substitute: naming
+
+//#if CYTHON_PEP489_MULTI_PHASE_INIT
+static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) {
+ #if PY_VERSION_HEX >= 0x030700A1
+ static PY_INT64_T main_interpreter_id = -1;
+ PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp);
+ if (main_interpreter_id == -1) {
+ main_interpreter_id = current_id;
+ return (unlikely(current_id == -1)) ? -1 : 0;
+ } else if (unlikely(main_interpreter_id != current_id))
+
+ #else
+ static PyInterpreterState *main_interpreter = NULL;
+ PyInterpreterState *current_interpreter = PyThreadState_Get()->interp;
+ if (!main_interpreter) {
+ main_interpreter = current_interpreter;
+ } else if (unlikely(main_interpreter != current_interpreter))
+ #endif
+
+ {
+ PyErr_SetString(
+ PyExc_ImportError,
+ "Interpreter change detected - this module can only be loaded into one interpreter per process.");
+ return -1;
+ }
+ return 0;
+}
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *module, const char* from_name, const char* to_name, int allow_none)
+#else
+static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none)
+#endif
+{
+ PyObject *value = PyObject_GetAttrString(spec, from_name);
+ int result = 0;
+ if (likely(value)) {
+ if (allow_none || value != Py_None) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ result = PyModule_AddObject(module, to_name, value);
+#else
+ result = PyDict_SetItemString(moddict, to_name, value);
+#endif
+ }
+ Py_DECREF(value);
+ } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ } else {
+ result = -1;
+ }
+ return result;
+}
+
+static CYTHON_SMALL_CODE PyObject* ${pymodule_create_func_cname}(PyObject *spec, PyModuleDef *def) {
+ PyObject *module = NULL, *moddict, *modname;
+ CYTHON_UNUSED_VAR(def);
+
+ // For now, we only have exactly one module instance.
+ if (__Pyx_check_single_interpreter())
+ return NULL;
+ if (${module_cname})
+ return __Pyx_NewRef(${module_cname});
+
+ modname = PyObject_GetAttrString(spec, "name");
+ if (unlikely(!modname)) goto bad;
+
+ module = PyModule_NewObject(modname);
+ Py_DECREF(modname);
+ if (unlikely(!module)) goto bad;
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+ moddict = module;
+#else
+ moddict = PyModule_GetDict(module);
+ if (unlikely(!moddict)) goto bad;
+ // moddict is a borrowed reference
+#endif
+
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad;
+
+ return module;
+bad:
+ Py_XDECREF(module);
+ return NULL;
+}
+//#endif
+
+
/////////////// CodeObjectCache.proto ///////////////
+#if !CYTHON_COMPILING_IN_LIMITED_API
typedef struct {
PyCodeObject* code_object;
int code_line;
@@ -522,11 +1442,13 @@
static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line);
static PyCodeObject *__pyx_find_code_object(int code_line);
static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
+#endif
/////////////// CodeObjectCache ///////////////
// Note that errors are simply ignored in the code below.
// This is just a cache, if a lookup or insertion fails - so what?
+#if !CYTHON_COMPILING_IN_LIMITED_API
static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) {
int start = 0, mid = 0, end = count - 1;
if (end >= 0 && code_line > entries[end].code_line) {
@@ -592,7 +1514,7 @@
if (__pyx_code_cache.count == __pyx_code_cache.max_count) {
int new_max = __pyx_code_cache.max_count + 64;
entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc(
- __pyx_code_cache.entries, (size_t)new_max*sizeof(__Pyx_CodeObjectCacheEntry));
+ __pyx_code_cache.entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry));
if (unlikely(!entries)) {
return;
}
@@ -607,9 +1529,11 @@
__pyx_code_cache.count++;
Py_INCREF(code_object);
}
+#endif
/////////////// CodeObjectCache.cleanup ///////////////
+ #if !CYTHON_COMPILING_IN_LIMITED_API
if (__pyx_code_cache.entries) {
__Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries;
int i, count = __pyx_code_cache.count;
@@ -621,6 +1545,7 @@
}
PyMem_Free(entries);
}
+ #endif
/////////////// CheckBinaryVersion.proto ///////////////
@@ -635,7 +1560,7 @@
if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) {
char message[200];
PyOS_snprintf(message, sizeof(message),
- "compiletime version %s of module '%.100s' "
+ "compile time version %s of module '%.100s' "
"does not match runtime version %s",
ctversion, __Pyx_MODULE_NAME, rtversion);
return PyErr_WarnEx(NULL, message, 1);
@@ -643,6 +1568,22 @@
return 0;
}
+/////////////// IsLittleEndian.proto ///////////////
+
+static CYTHON_INLINE int __Pyx_Is_Little_Endian(void);
+
+/////////////// IsLittleEndian ///////////////
+
+static CYTHON_INLINE int __Pyx_Is_Little_Endian(void)
+{
+ union {
+ uint32_t u32;
+ uint8_t u8[4];
+ } S;
+ S.u32 = 0x01020304;
+ return S.u8[0] == 4;
+}
+
/////////////// Refnanny.proto ///////////////
#ifndef CYTHON_REFNANNY
@@ -651,11 +1592,11 @@
#if CYTHON_REFNANNY
typedef struct {
- void (*INCREF)(void*, PyObject*, int);
- void (*DECREF)(void*, PyObject*, int);
- void (*GOTREF)(void*, PyObject*, int);
- void (*GIVEREF)(void*, PyObject*, int);
- void* (*SetupContext)(const char*, int, const char*);
+ void (*INCREF)(void*, PyObject*, Py_ssize_t);
+ void (*DECREF)(void*, PyObject*, Py_ssize_t);
+ void (*GOTREF)(void*, PyObject*, Py_ssize_t);
+ void (*GIVEREF)(void*, PyObject*, Py_ssize_t);
+ void* (*SetupContext)(const char*, Py_ssize_t, const char*);
void (*FinishContext)(void**);
} __Pyx_RefNannyAPIStruct;
static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL;
@@ -665,28 +1606,35 @@
#define __Pyx_RefNannySetupContext(name, acquire_gil) \
if (acquire_gil) { \
PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure(); \
- __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__)); \
PyGILState_Release(__pyx_gilstate_save); \
} else { \
- __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__); \
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__)); \
+ }
+ #define __Pyx_RefNannyFinishContextNogil() { \
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure(); \
+ __Pyx_RefNannyFinishContext(); \
+ PyGILState_Release(__pyx_gilstate_save); \
}
#else
#define __Pyx_RefNannySetupContext(name, acquire_gil) \
- __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__))
+ #define __Pyx_RefNannyFinishContextNogil() __Pyx_RefNannyFinishContext()
#endif
#define __Pyx_RefNannyFinishContext() \
__Pyx_RefNanny->FinishContext(&__pyx_refnanny)
- #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
- #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
- #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
- #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
- #define __Pyx_XINCREF(r) do { if((r) != NULL) {__Pyx_INCREF(r); }} while(0)
- #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r); }} while(0)
- #define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r); }} while(0)
- #define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);}} while(0)
+ #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0)
+ #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0)
+ #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0)
+ #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0)
#else
#define __Pyx_RefNannyDeclarations
#define __Pyx_RefNannySetupContext(name, acquire_gil)
+ #define __Pyx_RefNannyFinishContextNogil()
#define __Pyx_RefNannyFinishContext()
#define __Pyx_INCREF(r) Py_INCREF(r)
#define __Pyx_DECREF(r) Py_DECREF(r)
@@ -698,6 +1646,10 @@
#define __Pyx_XGIVEREF(r)
#endif /* CYTHON_REFNANNY */
+#define __Pyx_Py_XDECREF_SET(r, v) do { \
+ PyObject *tmp = (PyObject *) r; \
+ r = v; Py_XDECREF(tmp); \
+ } while (0)
#define __Pyx_XDECREF_SET(r, v) do { \
PyObject *tmp = (PyObject *) r; \
r = v; __Pyx_XDECREF(tmp); \
@@ -716,9 +1668,9 @@
static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) {
PyObject *m = NULL, *p = NULL;
void *r = NULL;
- m = PyImport_ImportModule((char *)modname);
+ m = PyImport_ImportModule(modname);
if (!m) goto end;
- p = PyObject_GetAttrString(m, (char *)"RefNannyAPI");
+ p = PyObject_GetAttrString(m, "RefNannyAPI");
if (!p) goto end;
r = PyLong_AsVoidPtr(p);
end:
@@ -728,18 +1680,37 @@
}
#endif /* CYTHON_REFNANNY */
+
+/////////////// ImportRefnannyAPI ///////////////
+
+#if CYTHON_REFNANNY
+__Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny");
+if (!__Pyx_RefNanny) {
+ PyErr_Clear();
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny");
+ if (!__Pyx_RefNanny)
+ Py_FatalError("failed to import 'refnanny' module");
+}
+#endif
+
+
/////////////// RegisterModuleCleanup.proto ///////////////
//@substitute: naming
static void ${cleanup_cname}(PyObject *self); /*proto*/
+
+#if PY_MAJOR_VERSION < 3 || CYTHON_COMPILING_IN_PYPY
static int __Pyx_RegisterCleanup(void); /*proto*/
+#else
+#define __Pyx_RegisterCleanup() (0)
+#endif
/////////////// RegisterModuleCleanup ///////////////
//@substitute: naming
-//@requires: ImportExport.c::ModuleImport
-#if PY_MAJOR_VERSION < 3
-static PyObject* ${cleanup_cname}_atexit(PyObject *module, CYTHON_UNUSED PyObject *unused) {
+#if PY_MAJOR_VERSION < 3 || CYTHON_COMPILING_IN_PYPY
+static PyObject* ${cleanup_cname}_atexit(PyObject *module, PyObject *unused) {
+ CYTHON_UNUSED_VAR(unused);
${cleanup_cname}(module);
Py_INCREF(Py_None); return Py_None;
}
@@ -766,7 +1737,7 @@
if (!cleanup_func)
goto bad;
- atexit = __Pyx_ImportModule("atexit");
+ atexit = PyImport_ImportModule("atexit");
if (!atexit)
goto bad;
reg = PyObject_GetAttrString(atexit, "_exithandlers");
@@ -808,10 +1779,200 @@
Py_XDECREF(res);
return ret;
}
+#endif
+
+/////////////// FastGil.init ///////////////
+#ifdef WITH_THREAD
+__Pyx_FastGilFuncInit();
+#endif
+
+/////////////// NoFastGil.proto ///////////////
+//@proto_block: utility_code_proto_before_types
+
+#define __Pyx_PyGILState_Ensure PyGILState_Ensure
+#define __Pyx_PyGILState_Release PyGILState_Release
+#define __Pyx_FastGIL_Remember()
+#define __Pyx_FastGIL_Forget()
+#define __Pyx_FastGilFuncInit()
+
+/////////////// FastGil.proto ///////////////
+//@proto_block: utility_code_proto_before_types
+
+#if CYTHON_FAST_GIL
+
+struct __Pyx_FastGilVtab {
+ PyGILState_STATE (*Fast_PyGILState_Ensure)(void);
+ void (*Fast_PyGILState_Release)(PyGILState_STATE oldstate);
+ void (*FastGIL_Remember)(void);
+ void (*FastGIL_Forget)(void);
+};
+
+static void __Pyx_FastGIL_Noop(void) {}
+static struct __Pyx_FastGilVtab __Pyx_FastGilFuncs = {
+ PyGILState_Ensure,
+ PyGILState_Release,
+ __Pyx_FastGIL_Noop,
+ __Pyx_FastGIL_Noop
+};
+
+static void __Pyx_FastGilFuncInit(void);
+
+#define __Pyx_PyGILState_Ensure __Pyx_FastGilFuncs.Fast_PyGILState_Ensure
+#define __Pyx_PyGILState_Release __Pyx_FastGilFuncs.Fast_PyGILState_Release
+#define __Pyx_FastGIL_Remember __Pyx_FastGilFuncs.FastGIL_Remember
+#define __Pyx_FastGIL_Forget __Pyx_FastGilFuncs.FastGIL_Forget
+
+#ifdef WITH_THREAD
+ #ifndef CYTHON_THREAD_LOCAL
+ #if __STDC_VERSION__ >= 201112
+ #define CYTHON_THREAD_LOCAL _Thread_local
+ #elif defined(__GNUC__)
+ #define CYTHON_THREAD_LOCAL __thread
+ #elif defined(_MSC_VER)
+ #define CYTHON_THREAD_LOCAL __declspec(thread)
+ #endif
+ #endif
+#endif
+
#else
-// fake call purely to work around "unused function" warning for __Pyx_ImportModule()
-static int __Pyx_RegisterCleanup(void) {
- if (0) __Pyx_ImportModule(NULL);
- return 0;
+#define __Pyx_PyGILState_Ensure PyGILState_Ensure
+#define __Pyx_PyGILState_Release PyGILState_Release
+#define __Pyx_FastGIL_Remember()
+#define __Pyx_FastGIL_Forget()
+#define __Pyx_FastGilFuncInit()
+#endif
+
+/////////////// FastGil ///////////////
+// The implementations of PyGILState_Ensure/Release calls PyThread_get_key_value
+// several times which is turns out to be quite slow (slower in fact than
+// acquiring the GIL itself). Simply storing it in a thread local for the
+// common case is much faster.
+// To make optimal use of this thread local, we attempt to share it between
+// modules.
+
+#if CYTHON_FAST_GIL
+
+#define __Pyx_FastGIL_ABI_module __PYX_ABI_MODULE_NAME
+#define __Pyx_FastGIL_PyCapsuleName "FastGilFuncs"
+#define __Pyx_FastGIL_PyCapsule \
+ __Pyx_FastGIL_ABI_module "." __Pyx_FastGIL_PyCapsuleName
+
+#ifdef CYTHON_THREAD_LOCAL
+
+#include "pythread.h"
+#include "pystate.h"
+
+static CYTHON_THREAD_LOCAL PyThreadState *__Pyx_FastGil_tcur = NULL;
+static CYTHON_THREAD_LOCAL int __Pyx_FastGil_tcur_depth = 0;
+static int __Pyx_FastGil_autoTLSkey = -1;
+
+static CYTHON_INLINE void __Pyx_FastGIL_Remember0(void) {
+ ++__Pyx_FastGil_tcur_depth;
+}
+
+static CYTHON_INLINE void __Pyx_FastGIL_Forget0(void) {
+ if (--__Pyx_FastGil_tcur_depth == 0) {
+ __Pyx_FastGil_tcur = NULL;
+ }
+}
+
+static CYTHON_INLINE PyThreadState *__Pyx_FastGil_get_tcur(void) {
+ PyThreadState *tcur = __Pyx_FastGil_tcur;
+ if (tcur == NULL) {
+ tcur = __Pyx_FastGil_tcur = (PyThreadState*)PyThread_get_key_value(__Pyx_FastGil_autoTLSkey);
+ }
+ return tcur;
+}
+
+static PyGILState_STATE __Pyx_FastGil_PyGILState_Ensure(void) {
+ int current;
+ PyThreadState *tcur;
+ __Pyx_FastGIL_Remember0();
+ tcur = __Pyx_FastGil_get_tcur();
+ if (tcur == NULL) {
+ // Uninitialized, need to initialize now.
+ return PyGILState_Ensure();
+ }
+ current = tcur == __Pyx_PyThreadState_Current;
+ if (current == 0) {
+ PyEval_RestoreThread(tcur);
+ }
+ ++tcur->gilstate_counter;
+ return current ? PyGILState_LOCKED : PyGILState_UNLOCKED;
+}
+
+static void __Pyx_FastGil_PyGILState_Release(PyGILState_STATE oldstate) {
+ PyThreadState *tcur = __Pyx_FastGil_get_tcur();
+ __Pyx_FastGIL_Forget0();
+ if (tcur->gilstate_counter == 1) {
+ // This is the last lock, do all the cleanup as well.
+ PyGILState_Release(oldstate);
+ } else {
+ --tcur->gilstate_counter;
+ if (oldstate == PyGILState_UNLOCKED) {
+ PyEval_SaveThread();
+ }
+ }
+}
+
+static void __Pyx_FastGilFuncInit0(void) {
+ /* Try to detect autoTLSkey. */
+ int key;
+ void* this_thread_state = (void*) PyGILState_GetThisThreadState();
+ for (key = 0; key < 100; key++) {
+ if (PyThread_get_key_value(key) == this_thread_state) {
+ __Pyx_FastGil_autoTLSkey = key;
+ break;
+ }
+ }
+ if (__Pyx_FastGil_autoTLSkey != -1) {
+ PyObject* capsule = NULL;
+ PyObject* abi_module = NULL;
+ __Pyx_PyGILState_Ensure = __Pyx_FastGil_PyGILState_Ensure;
+ __Pyx_PyGILState_Release = __Pyx_FastGil_PyGILState_Release;
+ __Pyx_FastGIL_Remember = __Pyx_FastGIL_Remember0;
+ __Pyx_FastGIL_Forget = __Pyx_FastGIL_Forget0;
+ capsule = PyCapsule_New(&__Pyx_FastGilFuncs, __Pyx_FastGIL_PyCapsule, NULL);
+ abi_module = PyImport_AddModule(__Pyx_FastGIL_ABI_module);
+ if (capsule && abi_module) {
+ PyObject_SetAttrString(abi_module, __Pyx_FastGIL_PyCapsuleName, capsule);
+ }
+ Py_XDECREF(capsule);
+ }
}
+
+#else
+
+static void __Pyx_FastGilFuncInit0(void) {
+}
+
+#endif
+
+static void __Pyx_FastGilFuncInit(void) {
+ struct __Pyx_FastGilVtab* shared = (struct __Pyx_FastGilVtab*)PyCapsule_Import(__Pyx_FastGIL_PyCapsule, 1);
+ if (shared) {
+ __Pyx_FastGilFuncs = *shared;
+ } else {
+ PyErr_Clear();
+ __Pyx_FastGilFuncInit0();
+ }
+}
+
+#endif
+
+///////////////////// UtilityCodePragmas /////////////////////////
+
+#if _MSC_VER
+#pragma warning( push )
+/* Warning 4127: conditional expression is constant
+ * Cython uses constant conditional expressions to allow in inline functions to be optimized at
+ * compile-time, so this warning is not useful
+ */
+#pragma warning( disable : 4127 )
+#endif
+
+///////////////////// UtilityCodePragmasEnd //////////////////////
+
+#if _MSV_VER
+#pragma warning( pop ) /* undo whatever Cython has done to warnings */
#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/NumpyImportArray.c cython-0.20.1+1~202203241016-9537/Cython/Utility/NumpyImportArray.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/NumpyImportArray.c 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/NumpyImportArray.c 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,21 @@
+///////////////////////// NumpyImportArray.init ////////////////////
+
+// comment below is deliberately kept in the generated C file to
+// help users debug where this came from:
+/*
+ * Cython has automatically inserted a call to _import_array since
+ * you didn't include one when you cimported numpy. To disable this
+ * add the line
+ * numpy._import_array
+ */
+#ifdef NPY_FEATURE_VERSION /* This is a public define that makes us reasonably confident it's "real" Numpy */
+// NO_IMPORT_ARRAY is Numpy's mechanism for indicating that import_array is handled elsewhere
+#if !NO_IMPORT_ARRAY /* https://docs.scipy.org/doc/numpy-1.17.0/reference/c-api.array.html#c.NO_IMPORT_ARRAY */
+if (unlikely(_import_array() == -1)) {
+ PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import "
+ "(auto-generated because you didn't call 'numpy.import_array()' after cimporting numpy; "
+ "use 'numpy._import_array' to disable if you are certain you don't need it).");
+ {{ err_goto }};
+}
+#endif
+#endif
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/ObjectHandling.c cython-0.20.1+1~202203241016-9537/Cython/Utility/ObjectHandling.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/ObjectHandling.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/ObjectHandling.c 2022-03-24 10:16:46.000000000 +0000
@@ -72,57 +72,76 @@
Py_DECREF(retval);
__Pyx_RaiseTooManyValuesError(expected);
return -1;
- } else {
- return __Pyx_IterFinish();
}
- return 0;
+
+ return __Pyx_IterFinish();
}
/////////////// UnpackTuple2.proto ///////////////
-static CYTHON_INLINE int __Pyx_unpack_tuple2(PyObject* tuple, PyObject** value1, PyObject** value2,
- int is_tuple, int has_known_size, int decref_tuple);
+#define __Pyx_unpack_tuple2(tuple, value1, value2, is_tuple, has_known_size, decref_tuple) \
+ (likely(is_tuple || PyTuple_Check(tuple)) ? \
+ (likely(has_known_size || PyTuple_GET_SIZE(tuple) == 2) ? \
+ __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple) : \
+ (__Pyx_UnpackTupleError(tuple, 2), -1)) : \
+ __Pyx_unpack_tuple2_generic(tuple, value1, value2, has_known_size, decref_tuple))
+
+static CYTHON_INLINE int __Pyx_unpack_tuple2_exact(
+ PyObject* tuple, PyObject** value1, PyObject** value2, int decref_tuple);
+static int __Pyx_unpack_tuple2_generic(
+ PyObject* tuple, PyObject** value1, PyObject** value2, int has_known_size, int decref_tuple);
/////////////// UnpackTuple2 ///////////////
//@requires: UnpackItemEndCheck
//@requires: UnpackTupleError
//@requires: RaiseNeedMoreValuesToUnpack
-static CYTHON_INLINE int __Pyx_unpack_tuple2(PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2,
- int is_tuple, int has_known_size, int decref_tuple) {
- Py_ssize_t index;
- PyObject *value1 = NULL, *value2 = NULL, *iter = NULL;
- if (!is_tuple && unlikely(!PyTuple_Check(tuple))) {
- iternextfunc iternext;
- iter = PyObject_GetIter(tuple);
- if (unlikely(!iter)) goto bad;
- if (decref_tuple) { Py_DECREF(tuple); tuple = NULL; }
- iternext = Py_TYPE(iter)->tp_iternext;
- value1 = iternext(iter); if (unlikely(!value1)) { index = 0; goto unpacking_failed; }
- value2 = iternext(iter); if (unlikely(!value2)) { index = 1; goto unpacking_failed; }
- if (!has_known_size && unlikely(__Pyx_IternextUnpackEndCheck(iternext(iter), 2))) goto bad;
- Py_DECREF(iter);
- } else {
- if (!has_known_size && unlikely(PyTuple_GET_SIZE(tuple) != 2)) {
- __Pyx_UnpackTupleError(tuple, 2);
- goto bad;
- }
+static CYTHON_INLINE int __Pyx_unpack_tuple2_exact(
+ PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2, int decref_tuple) {
+ PyObject *value1 = NULL, *value2 = NULL;
#if CYTHON_COMPILING_IN_PYPY
- value1 = PySequence_ITEM(tuple, 0);
- if (unlikely(!value1)) goto bad;
- value2 = PySequence_ITEM(tuple, 1);
- if (unlikely(!value2)) goto bad;
-#else
- value1 = PyTuple_GET_ITEM(tuple, 0);
- value2 = PyTuple_GET_ITEM(tuple, 1);
- Py_INCREF(value1);
- Py_INCREF(value2);
+ value1 = PySequence_ITEM(tuple, 0); if (unlikely(!value1)) goto bad;
+ value2 = PySequence_ITEM(tuple, 1); if (unlikely(!value2)) goto bad;
+#else
+ value1 = PyTuple_GET_ITEM(tuple, 0); Py_INCREF(value1);
+ value2 = PyTuple_GET_ITEM(tuple, 1); Py_INCREF(value2);
#endif
- if (decref_tuple) { Py_DECREF(tuple); }
+ if (decref_tuple) {
+ Py_DECREF(tuple);
}
+
+ *pvalue1 = value1;
+ *pvalue2 = value2;
+ return 0;
+#if CYTHON_COMPILING_IN_PYPY
+bad:
+ Py_XDECREF(value1);
+ Py_XDECREF(value2);
+ if (decref_tuple) { Py_XDECREF(tuple); }
+ return -1;
+#endif
+}
+
+static int __Pyx_unpack_tuple2_generic(PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2,
+ int has_known_size, int decref_tuple) {
+ Py_ssize_t index;
+ PyObject *value1 = NULL, *value2 = NULL, *iter = NULL;
+ iternextfunc iternext;
+
+ iter = PyObject_GetIter(tuple);
+ if (unlikely(!iter)) goto bad;
+ if (decref_tuple) { Py_DECREF(tuple); tuple = NULL; }
+
+ iternext = __Pyx_PyObject_GetIterNextFunc(iter);
+ value1 = iternext(iter); if (unlikely(!value1)) { index = 0; goto unpacking_failed; }
+ value2 = iternext(iter); if (unlikely(!value2)) { index = 1; goto unpacking_failed; }
+ if (!has_known_size && unlikely(__Pyx_IternextUnpackEndCheck(iternext(iter), 2))) goto bad;
+
+ Py_DECREF(iter);
*pvalue1 = value1;
*pvalue2 = value2;
return 0;
+
unpacking_failed:
if (!has_known_size && __Pyx_IterFinish() == 0)
__Pyx_RaiseNeedMoreValuesError(index);
@@ -134,49 +153,77 @@
return -1;
}
+
/////////////// IterNext.proto ///////////////
#define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL)
static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject *, PyObject *); /*proto*/
/////////////// IterNext ///////////////
+//@requires: Exceptions.c::PyThreadStateGet
+//@requires: Exceptions.c::PyErrFetchRestore
+
+static PyObject *__Pyx_PyIter_Next2Default(PyObject* defval) {
+ PyObject* exc_type;
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ exc_type = __Pyx_PyErr_Occurred();
+ if (unlikely(exc_type)) {
+ if (!defval || unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))
+ return NULL;
+ __Pyx_PyErr_Clear();
+ Py_INCREF(defval);
+ return defval;
+ }
+ if (defval) {
+ Py_INCREF(defval);
+ return defval;
+ }
+ __Pyx_PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static void __Pyx_PyIter_Next_ErrorNoIterator(PyObject *iterator) {
+ __Pyx_TypeName iterator_type_name = __Pyx_PyType_GetName(Py_TYPE(iterator));
+ PyErr_Format(PyExc_TypeError,
+ __Pyx_FMT_TYPENAME " object is not an iterator", iterator_type_name);
+ __Pyx_DECREF_TypeName(iterator_type_name);
+}
// originally copied from Py3's builtin_next()
static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject* iterator, PyObject* defval) {
PyObject* next;
+ // We always do a quick slot check because calling PyIter_Check() is so wasteful.
iternextfunc iternext = Py_TYPE(iterator)->tp_iternext;
+ if (likely(iternext)) {
#if CYTHON_USE_TYPE_SLOTS
- if (unlikely(!iternext)) {
+ next = iternext(iterator);
+ if (likely(next))
+ return next;
+ if (unlikely(iternext == &_PyObject_NextNotImplemented))
+ return NULL;
#else
- if (unlikely(!iternext) || unlikely(!PyIter_Check(iterator))) {
-#endif
- PyErr_Format(PyExc_TypeError,
- "%.200s object is not an iterator", Py_TYPE(iterator)->tp_name);
+ // Since the slot was set, assume that PyIter_Next() will likely succeed, and properly fail otherwise.
+ // Note: PyIter_Next() crashes in CPython if "tp_iternext" is NULL.
+ next = PyIter_Next(iterator);
+ if (likely(next))
+ return next;
+#endif
+ } else if (CYTHON_USE_TYPE_SLOTS || unlikely(!PyIter_Check(iterator))) {
+ // If CYTHON_USE_TYPE_SLOTS, then the slot was not set and we don't have an iterable.
+ // Otherwise, don't trust "tp_iternext" and rely on PyIter_Check().
+ __Pyx_PyIter_Next_ErrorNoIterator(iterator);
return NULL;
}
- next = iternext(iterator);
- if (likely(next))
- return next;
-#if CYTHON_USE_TYPE_SLOTS
-#if PY_VERSION_HEX >= 0x02070000
- if (unlikely(iternext == &_PyObject_NextNotImplemented))
- return NULL;
-#endif
-#endif
- if (defval) {
- PyObject* exc_type = PyErr_Occurred();
- if (exc_type) {
- if (unlikely(exc_type != PyExc_StopIteration) &&
- !PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))
- return NULL;
- PyErr_Clear();
- }
- Py_INCREF(defval);
- return defval;
+#if !CYTHON_USE_TYPE_SLOTS
+ else {
+ // We have an iterator with an empty "tp_iternext", but didn't call next() on it yet.
+ next = PyIter_Next(iterator);
+ if (likely(next))
+ return next;
}
- if (!PyErr_Occurred())
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+#endif
+ return __Pyx_PyIter_Next2Default(defval);
}
/////////////// IterFinish.proto ///////////////
@@ -191,10 +238,10 @@
static CYTHON_INLINE int __Pyx_IterFinish(void) {
#if CYTHON_FAST_THREAD_STATE
- PyThreadState *tstate = PyThreadState_GET();
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
PyObject* exc_type = tstate->curexc_type;
if (unlikely(exc_type)) {
- if (likely(exc_type == PyExc_StopIteration) || PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)) {
+ if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) {
PyObject *exc_value, *exc_tb;
exc_value = tstate->curexc_value;
exc_tb = tstate->curexc_traceback;
@@ -223,29 +270,119 @@
#endif
}
+
+/////////////// ObjectGetItem.proto ///////////////
+
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key);/*proto*/
+#else
+#define __Pyx_PyObject_GetItem(obj, key) PyObject_GetItem(obj, key)
+#endif
+
+/////////////// ObjectGetItem ///////////////
+// //@requires: GetItemInt - added in IndexNode as it uses templating.
+//@requires: PyObjectGetAttrStrNoError
+//@requires: PyObjectCallOneArg
+
+#if CYTHON_USE_TYPE_SLOTS
+static PyObject *__Pyx_PyObject_GetIndex(PyObject *obj, PyObject *index) {
+ // Get element from sequence object `obj` at index `index`.
+ PyObject *runerr;
+ Py_ssize_t key_value;
+ key_value = __Pyx_PyIndex_AsSsize_t(index);
+ if (likely(key_value != -1 || !(runerr = PyErr_Occurred()))) {
+ return __Pyx_GetItemInt_Fast(obj, key_value, 0, 1, 1);
+ }
+
+ // Error handling code -- only manage OverflowError differently.
+ if (PyErr_GivenExceptionMatches(runerr, PyExc_OverflowError)) {
+ __Pyx_TypeName index_type_name = __Pyx_PyType_GetName(Py_TYPE(index));
+ PyErr_Clear();
+ PyErr_Format(PyExc_IndexError,
+ "cannot fit '" __Pyx_FMT_TYPENAME "' into an index-sized integer", index_type_name);
+ __Pyx_DECREF_TypeName(index_type_name);
+ }
+ return NULL;
+}
+
+static PyObject *__Pyx_PyObject_GetItem_Slow(PyObject *obj, PyObject *key) {
+ __Pyx_TypeName obj_type_name;
+ // Handles less common slow-path checks for GetItem
+ if (likely(PyType_Check(obj))) {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(obj, PYIDENT("__class_getitem__"));
+ if (meth) {
+ PyObject *result = __Pyx_PyObject_CallOneArg(meth, key);
+ Py_DECREF(meth);
+ return result;
+ }
+ }
+
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is not subscriptable", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return NULL;
+}
+
+static PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key) {
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyMappingMethods *mm = tp->tp_as_mapping;
+ PySequenceMethods *sm = tp->tp_as_sequence;
+
+ if (likely(mm && mm->mp_subscript)) {
+ return mm->mp_subscript(obj, key);
+ }
+ if (likely(sm && sm->sq_item)) {
+ return __Pyx_PyObject_GetIndex(obj, key);
+ }
+ return __Pyx_PyObject_GetItem_Slow(obj, key);
+}
+#endif
+
+
/////////////// DictGetItem.proto ///////////////
#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
+static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key);/*proto*/
+
+#define __Pyx_PyObject_Dict_GetItem(obj, name) \
+ (likely(PyDict_CheckExact(obj)) ? \
+ __Pyx_PyDict_GetItem(obj, name) : PyObject_GetItem(obj, name))
+
+#else
+#define __Pyx_PyDict_GetItem(d, key) PyObject_GetItem(d, key)
+#define __Pyx_PyObject_Dict_GetItem(obj, name) PyObject_GetItem(obj, name)
+#endif
+
+/////////////// DictGetItem ///////////////
+
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key) {
PyObject *value;
value = PyDict_GetItemWithError(d, key);
if (unlikely(!value)) {
if (!PyErr_Occurred()) {
- PyObject* args = PyTuple_Pack(1, key);
- if (likely(args))
- PyErr_SetObject(PyExc_KeyError, args);
- Py_XDECREF(args);
+ if (unlikely(PyTuple_Check(key))) {
+ // CPython interprets tuples as separate arguments => must wrap them in another tuple.
+ PyObject* args = PyTuple_Pack(1, key);
+ if (likely(args)) {
+ PyErr_SetObject(PyExc_KeyError, args);
+ Py_DECREF(args);
+ }
+ } else {
+ // Avoid tuple packing if possible.
+ PyErr_SetObject(PyExc_KeyError, key);
+ }
}
return NULL;
}
Py_INCREF(value);
return value;
}
-#else
- #define __Pyx_PyDict_GetItem(d, key) PyObject_GetItem(d, key)
#endif
/////////////// GetItemInt.proto ///////////////
+//@substitute: tempita
#define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \
(__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \
@@ -263,15 +400,16 @@
int wraparound, int boundscheck);
{{endfor}}
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j);
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j);
static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
int is_list, int wraparound, int boundscheck);
/////////////// GetItemInt ///////////////
+//@substitute: tempita
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
PyObject *r;
- if (!j) return NULL;
+ if (unlikely(!j)) return NULL;
r = PyObject_GetItem(o, j);
Py_DECREF(j);
return r;
@@ -282,9 +420,12 @@
CYTHON_NCP_UNUSED int wraparound,
CYTHON_NCP_UNUSED int boundscheck) {
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
- if (wraparound & unlikely(i < 0)) i += Py{{type}}_GET_SIZE(o);
- if ((!boundscheck) || likely((0 <= i) & (i < Py{{type}}_GET_SIZE(o)))) {
- PyObject *r = Py{{type}}_GET_ITEM(o, i);
+ Py_ssize_t wrapped_i = i;
+ if (wraparound & unlikely(i < 0)) {
+ wrapped_i += Py{{type}}_GET_SIZE(o);
+ }
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, Py{{type}}_GET_SIZE(o)))) {
+ PyObject *r = Py{{type}}_GET_ITEM(o, wrapped_i);
Py_INCREF(r);
return r;
}
@@ -301,7 +442,7 @@
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS
if (is_list || PyList_CheckExact(o)) {
Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o);
- if ((!boundscheck) || (likely((n >= 0) & (n < PyList_GET_SIZE(o))))) {
+ if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) {
PyObject *r = PyList_GET_ITEM(o, n);
Py_INCREF(r);
return r;
@@ -309,17 +450,25 @@
}
else if (PyTuple_CheckExact(o)) {
Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o);
- if ((!boundscheck) || likely((n >= 0) & (n < PyTuple_GET_SIZE(o)))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) {
PyObject *r = PyTuple_GET_ITEM(o, n);
Py_INCREF(r);
return r;
}
} else {
// inlined PySequence_GetItem() + special cased length overflow
- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence;
- if (likely(m && m->sq_item)) {
- if (wraparound && unlikely(i < 0) && likely(m->sq_length)) {
- Py_ssize_t l = m->sq_length(o);
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if (mm && mm->mp_subscript) {
+ PyObject *r, *key = PyInt_FromSsize_t(i);
+ if (unlikely(!key)) return NULL;
+ r = mm->mp_subscript(o, key);
+ Py_DECREF(key);
+ return r;
+ }
+ if (likely(sm && sm->sq_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
if (likely(l >= 0)) {
i += l;
} else {
@@ -329,7 +478,7 @@
PyErr_Clear();
}
}
- return m->sq_item(o, i);
+ return sm->sq_item(o, i);
}
}
#else
@@ -348,15 +497,15 @@
(is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) : \
__Pyx_SetItemInt_Generic(o, to_py_func(i), v)))
-static CYTHON_INLINE int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v);
+static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v);
static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v,
int is_list, int wraparound, int boundscheck);
/////////////// SetItemInt ///////////////
-static CYTHON_INLINE int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) {
+static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) {
int r;
- if (!j) return -1;
+ if (unlikely(!j)) return -1;
r = PyObject_SetItem(o, j, v);
Py_DECREF(j);
return r;
@@ -367,7 +516,7 @@
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS
if (is_list || PyList_CheckExact(o)) {
Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o));
- if ((!boundscheck) || likely((n >= 0) & (n < PyList_GET_SIZE(o)))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) {
PyObject* old = PyList_GET_ITEM(o, n);
Py_INCREF(v);
PyList_SET_ITEM(o, n, v);
@@ -376,10 +525,19 @@
}
} else {
// inlined PySequence_SetItem() + special cased length overflow
- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence;
- if (likely(m && m->sq_ass_item)) {
- if (wraparound && unlikely(i < 0) && likely(m->sq_length)) {
- Py_ssize_t l = m->sq_length(o);
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if (mm && mm->mp_ass_subscript) {
+ int r;
+ PyObject *key = PyInt_FromSsize_t(i);
+ if (unlikely(!key)) return -1;
+ r = mm->mp_ass_subscript(o, key, v);
+ Py_DECREF(key);
+ return r;
+ }
+ if (likely(sm && sm->sq_ass_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
if (likely(l >= 0)) {
i += l;
} else {
@@ -389,15 +547,16 @@
PyErr_Clear();
}
}
- return m->sq_ass_item(o, i, v);
+ return sm->sq_ass_item(o, i, v);
}
}
#else
#if CYTHON_COMPILING_IN_PYPY
- if (is_list || (PySequence_Check(o) && !PyDict_Check(o))) {
+ if (is_list || (PySequence_Check(o) && !PyDict_Check(o)))
#else
- if (is_list || PySequence_Check(o)) {
+ if (is_list || PySequence_Check(o))
#endif
+ {
return PySequence_SetItem(o, i, v);
}
#endif
@@ -413,32 +572,37 @@
(is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) : \
__Pyx_DelItem_Generic(o, to_py_func(i))))
-static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j);
+static int __Pyx_DelItem_Generic(PyObject *o, PyObject *j);
static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i,
int is_list, int wraparound);
/////////////// DelItemInt ///////////////
-static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j) {
+static int __Pyx_DelItem_Generic(PyObject *o, PyObject *j) {
int r;
- if (!j) return -1;
+ if (unlikely(!j)) return -1;
r = PyObject_DelItem(o, j);
Py_DECREF(j);
return r;
}
static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i,
- CYTHON_UNUSED int is_list, CYTHON_NCP_UNUSED int wraparound) {
+ int is_list, CYTHON_NCP_UNUSED int wraparound) {
#if !CYTHON_USE_TYPE_SLOTS
if (is_list || PySequence_Check(o)) {
return PySequence_DelItem(o, i);
}
#else
// inlined PySequence_DelItem() + special cased length overflow
- PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence;
- if (likely(m && m->sq_ass_item)) {
- if (wraparound && unlikely(i < 0) && likely(m->sq_length)) {
- Py_ssize_t l = m->sq_length(o);
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if ((!is_list) && mm && mm->mp_ass_subscript) {
+ PyObject *key = PyInt_FromSsize_t(i);
+ return likely(key) ? mm->mp_ass_subscript(o, key, (PyObject *)NULL) : -1;
+ }
+ if (likely(sm && sm->sq_ass_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
if (likely(l >= 0)) {
i += l;
} else {
@@ -448,7 +612,7 @@
PyErr_Clear();
}
}
- return m->sq_ass_item(o, i, (PyObject *)NULL);
+ return sm->sq_ass_item(o, i, (PyObject *)NULL);
}
#endif
return __Pyx_DelItem_Generic(o, PyInt_FromSsize_t(i));
@@ -483,7 +647,8 @@
{{endif}}
Py_ssize_t cstart, Py_ssize_t cstop,
PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice,
- int has_cstart, int has_cstop, CYTHON_UNUSED int wraparound) {
+ int has_cstart, int has_cstop, int wraparound) {
+ __Pyx_TypeName obj_type_name;
#if CYTHON_USE_TYPE_SLOTS
PyMappingMethods* mp;
#if PY_MAJOR_VERSION < 3
@@ -527,6 +692,8 @@
return ms->sq_ass_slice(obj, cstart, cstop, value);
{{endif}}
}
+#else
+ CYTHON_UNUSED_VAR(wraparound);
#endif
mp = Py_TYPE(obj)->tp_as_mapping;
@@ -535,6 +702,8 @@
{{else}}
if (likely(mp && mp->mp_ass_subscript))
{{endif}}
+#else
+ CYTHON_UNUSED_VAR(wraparound);
#endif
{
{{if access == 'Get'}}PyObject*{{else}}int{{endif}} result;
@@ -586,19 +755,70 @@
}
return result;
}
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
PyErr_Format(PyExc_TypeError,
{{if access == 'Get'}}
- "'%.200s' object is unsliceable", Py_TYPE(obj)->tp_name);
+ "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name);
{{else}}
- "'%.200s' object does not support slice %.10s",
- Py_TYPE(obj)->tp_name, value ? "assignment" : "deletion");
+ "'" __Pyx_FMT_TYPENAME "' object does not support slice %.10s",
+ obj_type_name, value ? "assignment" : "deletion");
{{endif}}
+ __Pyx_DECREF_TypeName(obj_type_name);
bad:
return {{if access == 'Get'}}NULL{{else}}-1{{endif}};
}
+/////////////// TupleAndListFromArray.proto ///////////////
+
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n);
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n);
+#endif
+
+/////////////// TupleAndListFromArray ///////////////
+//@substitute: naming
+
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) {
+ PyObject *v;
+ Py_ssize_t i;
+ for (i = 0; i < length; i++) {
+ v = dest[i] = src[i];
+ Py_INCREF(v);
+ }
+}
+
+static CYTHON_INLINE PyObject *
+__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ Py_INCREF($empty_tuple);
+ return $empty_tuple;
+ }
+ res = PyTuple_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n);
+ return res;
+}
+
+static CYTHON_INLINE PyObject *
+__Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ return PyList_New(0);
+ }
+ res = PyList_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n);
+ return res;
+}
+#endif
+
+
/////////////// SliceTupleAndList.proto ///////////////
#if CYTHON_COMPILING_IN_CPYTHON
@@ -610,6 +830,8 @@
#endif
/////////////// SliceTupleAndList ///////////////
+//@requires: TupleAndListFromArray
+//@substitute: tempita
#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE void __Pyx_crop_slice(Py_ssize_t* _start, Py_ssize_t* _stop, Py_ssize_t* _length) {
@@ -630,32 +852,12 @@
*_stop = stop;
}
-static CYTHON_INLINE void __Pyx_copy_object_array(PyObject** CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) {
- PyObject *v;
- Py_ssize_t i;
- for (i = 0; i < length; i++) {
- v = dest[i] = src[i];
- Py_INCREF(v);
- }
-}
-
{{for type in ['List', 'Tuple']}}
static CYTHON_INLINE PyObject* __Pyx_Py{{type}}_GetSlice(
PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
- PyObject* dest;
Py_ssize_t length = Py{{type}}_GET_SIZE(src);
__Pyx_crop_slice(&start, &stop, &length);
- if (unlikely(length <= 0))
- return Py{{type}}_New(0);
-
- dest = Py{{type}}_New(length);
- if (unlikely(!dest))
- return NULL;
- __Pyx_copy_object_array(
- ((Py{{type}}Object*)src)->ob_item + start,
- ((Py{{type}}Object*)dest)->ob_item,
- length);
- return dest;
+ return __Pyx_Py{{type}}_FromArray(((Py{{type}}Object*)src)->ob_item + start, length);
}
{{endfor}}
#endif
@@ -766,7 +968,7 @@
//@requires: CalculateMetaclass
static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) {
- PyObject *metaclass = mkw ? PyDict_GetItem(mkw, PYIDENT("metaclass")) : NULL;
+ PyObject *metaclass = mkw ? __Pyx_PyDict_GetItemStr(mkw, PYIDENT("metaclass")) : NULL;
if (metaclass) {
Py_INCREF(metaclass);
if (PyDict_DelItem(mkw, PYIDENT("metaclass")) < 0) {
@@ -797,13 +999,17 @@
PyObject *result;
PyObject *metaclass;
- if (PyDict_SetItem(dict, PYIDENT("__module__"), modname) < 0)
+ if (unlikely(PyDict_SetItem(dict, PYIDENT("__module__"), modname) < 0))
return NULL;
- if (PyDict_SetItem(dict, PYIDENT("__qualname__"), qualname) < 0)
+#if PY_VERSION_HEX >= 0x03030000
+ if (unlikely(PyDict_SetItem(dict, PYIDENT("__qualname__"), qualname) < 0))
return NULL;
+#else
+ CYTHON_MAYBE_UNUSED_VAR(qualname);
+#endif
/* Python2 __metaclass__ */
- metaclass = PyDict_GetItem(dict, PYIDENT("__metaclass__"));
+ metaclass = __Pyx_PyDict_GetItemStr(dict, PYIDENT("__metaclass__"));
if (metaclass) {
Py_INCREF(metaclass);
if (PyType_Check(metaclass)) {
@@ -821,6 +1027,94 @@
return result;
}
+/////////////// Py3UpdateBases.proto ///////////////
+
+static PyObject* __Pyx_PEP560_update_bases(PyObject *bases); /* proto */
+
+/////////////// Py3UpdateBases /////////////////////
+//@requires: PyObjectCallOneArg
+//@requires: PyObjectGetAttrStrNoError
+
+/* Shamelessly adapted from cpython/bltinmodule.c update_bases */
+static PyObject*
+__Pyx_PEP560_update_bases(PyObject *bases)
+{
+ Py_ssize_t i, j, size_bases;
+ PyObject *base, *meth, *new_base, *result, *new_bases = NULL;
+ /*assert(PyTuple_Check(bases));*/
+
+ size_bases = PyTuple_GET_SIZE(bases);
+ for (i = 0; i < size_bases; i++) {
+ // original code in CPython: base = args[i];
+ base = PyTuple_GET_ITEM(bases, i);
+ if (PyType_Check(base)) {
+ if (new_bases) {
+ // If we already have made a replacement, then we append every normal base,
+ // otherwise just skip it.
+ if (PyList_Append(new_bases, base) < 0) {
+ goto error;
+ }
+ }
+ continue;
+ }
+ // original code in CPython:
+ // if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) {
+ meth = __Pyx_PyObject_GetAttrStrNoError(base, PYIDENT("__mro_entries__"));
+ if (!meth && PyErr_Occurred()) {
+ goto error;
+ }
+ if (!meth) {
+ if (new_bases) {
+ if (PyList_Append(new_bases, base) < 0) {
+ goto error;
+ }
+ }
+ continue;
+ }
+ new_base = __Pyx_PyObject_CallOneArg(meth, bases);
+ Py_DECREF(meth);
+ if (!new_base) {
+ goto error;
+ }
+ if (!PyTuple_Check(new_base)) {
+ PyErr_SetString(PyExc_TypeError,
+ "__mro_entries__ must return a tuple");
+ Py_DECREF(new_base);
+ goto error;
+ }
+ if (!new_bases) {
+ // If this is a first successful replacement, create new_bases list and
+ // copy previously encountered bases.
+ if (!(new_bases = PyList_New(i))) {
+ goto error;
+ }
+ for (j = 0; j < i; j++) {
+ // original code in CPython: base = args[j];
+ base = PyTuple_GET_ITEM(bases, j);
+ PyList_SET_ITEM(new_bases, j, base);
+ Py_INCREF(base);
+ }
+ }
+ j = PyList_GET_SIZE(new_bases);
+ if (PyList_SetSlice(new_bases, j, j, new_base) < 0) {
+ goto error;
+ }
+ Py_DECREF(new_base);
+ }
+ if (!new_bases) {
+ // unlike the CPython implementation, always return a new reference
+ Py_INCREF(bases);
+ return bases;
+ }
+ result = PyList_AsTuple(new_bases);
+ Py_DECREF(new_bases);
+ return result;
+
+error:
+ Py_XDECREF(new_bases);
+ return NULL;
+}
+
/////////////// Py3ClassCreate.proto ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname,
@@ -829,27 +1123,27 @@
PyObject *mkw, int calculate_metaclass, int allow_py2_metaclass); /*proto*/
/////////////// Py3ClassCreate ///////////////
-//@requires: PyObjectGetAttrStr
+//@substitute: naming
+//@requires: PyObjectGetAttrStrNoError
//@requires: CalculateMetaclass
+//@requires: PyObjectFastCall
+//@requires: PyObjectCall2Args
+//@requires: PyObjectLookupSpecial
+// only in fallback code:
+//@requires: GetBuiltinName
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name,
PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) {
PyObject *ns;
if (metaclass) {
- PyObject *prep = __Pyx_PyObject_GetAttrStr(metaclass, PYIDENT("__prepare__"));
+ PyObject *prep = __Pyx_PyObject_GetAttrStrNoError(metaclass, PYIDENT("__prepare__"));
if (prep) {
- PyObject *pargs = PyTuple_Pack(2, name, bases);
- if (unlikely(!pargs)) {
- Py_DECREF(prep);
- return NULL;
- }
- ns = PyObject_Call(prep, pargs, mkw);
+ PyObject *pargs[3] = {NULL, name, bases};
+ ns = __Pyx_PyObject_FastCallDict(prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw);
Py_DECREF(prep);
- Py_DECREF(pargs);
} else {
- if (unlikely(!PyErr_ExceptionMatches(PyExc_AttributeError)))
+ if (unlikely(PyErr_Occurred()))
return NULL;
- PyErr_Clear();
ns = PyDict_New();
}
} else {
@@ -861,7 +1155,11 @@
/* Required here to emulate assignment order */
if (unlikely(PyObject_SetItem(ns, PYIDENT("__module__"), modname) < 0)) goto bad;
+#if PY_VERSION_HEX >= 0x03030000
if (unlikely(PyObject_SetItem(ns, PYIDENT("__qualname__"), qualname) < 0)) goto bad;
+#else
+ CYTHON_MAYBE_UNUSED_VAR(qualname);
+#endif
if (unlikely(doc && PyObject_SetItem(ns, PYIDENT("__doc__"), doc) < 0)) goto bad;
return ns;
bad:
@@ -869,11 +1167,164 @@
return NULL;
}
+#if PY_VERSION_HEX < 0x030600A4 && CYTHON_PEP487_INIT_SUBCLASS
+// https://www.python.org/dev/peps/pep-0487/
+static int __Pyx_SetNamesPEP487(PyObject *type_obj) {
+ PyTypeObject *type = (PyTypeObject*) type_obj;
+ PyObject *names_to_set, *key, *value, *set_name, *tmp;
+ Py_ssize_t i = 0;
+
+#if CYTHON_USE_TYPE_SLOTS
+ names_to_set = PyDict_Copy(type->tp_dict);
+#else
+ {
+ PyObject *d = PyObject_GetAttr(type_obj, PYIDENT("__dict__"));
+ names_to_set = NULL;
+ if (likely(d)) {
+ // d may not be a dict, e.g. PyDictProxy in PyPy2.
+ PyObject *names_to_set = PyDict_New();
+ int ret = likely(names_to_set) ? PyDict_Update(names_to_set, d) : -1;
+ Py_DECREF(d);
+ if (unlikely(ret < 0))
+ Py_CLEAR(names_to_set);
+ }
+ }
+#endif
+ if (unlikely(names_to_set == NULL))
+ goto bad;
+
+ while (PyDict_Next(names_to_set, &i, &key, &value)) {
+ set_name = __Pyx_PyObject_LookupSpecialNoError(value, PYIDENT("__set_name__"));
+ if (unlikely(set_name != NULL)) {
+ tmp = __Pyx_PyObject_Call2Args(set_name, type_obj, key);
+ Py_DECREF(set_name);
+ if (unlikely(tmp == NULL)) {
+ __Pyx_TypeName value_type_name =
+ __Pyx_PyType_GetName(Py_TYPE(value));
+ __Pyx_TypeName type_name = __Pyx_PyType_GetName(type);
+ PyErr_Format(PyExc_RuntimeError,
+#if PY_MAJOR_VERSION >= 3
+ "Error calling __set_name__ on '" __Pyx_FMT_TYPENAME "' instance %R " "in '" __Pyx_FMT_TYPENAME "'",
+ value_type_name, key, type_name);
+#else
+ "Error calling __set_name__ on '" __Pyx_FMT_TYPENAME "' instance %.100s in '" __Pyx_FMT_TYPENAME "'",
+ value_type_name,
+ PyString_Check(key) ? PyString_AS_STRING(key) : "?",
+ type_name);
+#endif
+ goto bad;
+ } else {
+ Py_DECREF(tmp);
+ }
+ }
+ else if (unlikely(PyErr_Occurred())) {
+ goto bad;
+ }
+ }
+
+ Py_DECREF(names_to_set);
+ return 0;
+bad:
+ Py_XDECREF(names_to_set);
+ return -1;
+}
+
+static PyObject *__Pyx_InitSubclassPEP487(PyObject *type_obj, PyObject *mkw) {
+#if CYTHON_USE_TYPE_SLOTS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+// Stripped-down version of "super(type_obj, type_obj).__init_subclass__(**mkw)" in CPython 3.8.
+ PyTypeObject *type = (PyTypeObject*) type_obj;
+ PyObject *mro = type->tp_mro;
+ Py_ssize_t i, nbases;
+ if (unlikely(!mro)) goto done;
+
+ // avoid "unused" warning
+ (void) &__Pyx_GetBuiltinName;
+
+ Py_INCREF(mro);
+ nbases = PyTuple_GET_SIZE(mro);
+
+ // Skip over the type itself and 'object'.
+ assert(PyTuple_GET_ITEM(mro, 0) == type_obj);
+ for (i = 1; i < nbases-1; i++) {
+ PyObject *base, *dict, *meth;
+ base = PyTuple_GET_ITEM(mro, i);
+ dict = ((PyTypeObject *)base)->tp_dict;
+ meth = __Pyx_PyDict_GetItemStrWithError(dict, PYIDENT("__init_subclass__"));
+ if (unlikely(meth)) {
+ descrgetfunc f = Py_TYPE(meth)->tp_descr_get;
+ PyObject *res;
+ Py_INCREF(meth);
+ if (likely(f)) {
+ res = f(meth, NULL, type_obj);
+ Py_DECREF(meth);
+ if (unlikely(!res)) goto bad;
+ meth = res;
+ }
+ res = __Pyx_PyObject_FastCallDict(meth, NULL, 0, mkw);
+ Py_DECREF(meth);
+ if (unlikely(!res)) goto bad;
+ Py_DECREF(res);
+ goto done;
+ } else if (unlikely(PyErr_Occurred())) {
+ goto bad;
+ }
+ }
+
+done:
+ Py_XDECREF(mro);
+ return type_obj;
+
+bad:
+ Py_XDECREF(mro);
+ Py_DECREF(type_obj);
+ return NULL;
+
+// CYTHON_USE_TYPE_SLOTS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+#else
+// Generic fallback: "super(type_obj, type_obj).__init_subclass__(**mkw)", as used in CPython 3.8.
+ PyObject *super_type, *super, *func, *res;
+
+#if CYTHON_COMPILING_IN_PYPY && !defined(PySuper_Type)
+ super_type = __Pyx_GetBuiltinName(PYIDENT("super"));
+#else
+ super_type = (PyObject*) &PySuper_Type;
+ // avoid "unused" warning
+ (void) &__Pyx_GetBuiltinName;
+#endif
+ super = likely(super_type) ? __Pyx_PyObject_Call2Args(super_type, type_obj, type_obj) : NULL;
+#if CYTHON_COMPILING_IN_PYPY && !defined(PySuper_Type)
+ Py_XDECREF(super_type);
+#endif
+ if (unlikely(!super)) {
+ Py_CLEAR(type_obj);
+ goto done;
+ }
+ func = __Pyx_PyObject_GetAttrStrNoError(super, PYIDENT("__init_subclass__"));
+ Py_DECREF(super);
+ if (likely(!func)) {
+ if (unlikely(PyErr_Occurred()))
+ Py_CLEAR(type_obj);
+ goto done;
+ }
+ res = __Pyx_PyObject_FastCallDict(func, NULL, 0, mkw);
+ Py_DECREF(func);
+ if (unlikely(!res))
+ Py_CLEAR(type_obj);
+ Py_XDECREF(res);
+done:
+ return type_obj;
+#endif
+}
+
+// PY_VERSION_HEX < 0x030600A4 && CYTHON_PEP487_INIT_SUBCLASS
+#endif
+
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases,
PyObject *dict, PyObject *mkw,
int calculate_metaclass, int allow_py2_metaclass) {
- PyObject *result, *margs;
+ PyObject *result;
PyObject *owned_metaclass = NULL;
+ PyObject *margs[4] = {NULL, name, bases, dict};
if (allow_py2_metaclass) {
/* honour Python2 __metaclass__ for backward compatibility */
owned_metaclass = PyObject_GetItem(dict, PYIDENT("__metaclass__"));
@@ -892,14 +1343,28 @@
return NULL;
owned_metaclass = metaclass;
}
- margs = PyTuple_Pack(3, name, bases, dict);
- if (unlikely(!margs)) {
- result = NULL;
- } else {
- result = PyObject_Call(metaclass, margs, mkw);
- Py_DECREF(margs);
- }
+ result = __Pyx_PyObject_FastCallDict(metaclass, margs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET,
+#if PY_VERSION_HEX < 0x030600A4
+ // Before PEP-487, type(a,b,c) did not accept any keyword arguments, so guard at least against that case.
+ (metaclass == (PyObject*)&PyType_Type) ? NULL : mkw
+#else
+ mkw
+#endif
+ );
Py_XDECREF(owned_metaclass);
+
+#if PY_VERSION_HEX < 0x030600A4 && CYTHON_PEP487_INIT_SUBCLASS
+ if (likely(result) && likely(PyType_Check(result))) {
+ if (unlikely(__Pyx_SetNamesPEP487(result) < 0)) {
+ Py_CLEAR(result);
+ } else {
+ result = __Pyx_InitSubclassPEP487(result, mkw);
+ }
+ }
+#else
+ // avoid "unused" warning
+ (void) &__Pyx_GetBuiltinName;
+#endif
return result;
}
@@ -910,21 +1375,28 @@
/////////////// ExtTypeTest ///////////////
static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
+ __Pyx_TypeName obj_type_name;
+ __Pyx_TypeName type_name;
if (unlikely(!type)) {
PyErr_SetString(PyExc_SystemError, "Missing type object");
return 0;
}
- if (likely(PyObject_TypeCheck(obj, type)))
+ if (likely(__Pyx_TypeCheck(obj, type)))
return 1;
- PyErr_Format(PyExc_TypeError, "Cannot convert %.200s to %.200s",
- Py_TYPE(obj)->tp_name, type->tp_name);
+ obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ type_name = __Pyx_PyType_GetName(type);
+ PyErr_Format(PyExc_TypeError,
+ "Cannot convert " __Pyx_FMT_TYPENAME " to " __Pyx_FMT_TYPENAME,
+ obj_type_name, type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ __Pyx_DECREF_TypeName(type_name);
return 0;
}
/////////////// CallableCheck.proto ///////////////
#if CYTHON_USE_TYPE_SLOTS && PY_MAJOR_VERSION >= 3
-#define __Pyx_PyCallable_Check(obj) ((obj)->ob_type->tp_call != NULL)
+#define __Pyx_PyCallable_Check(obj) (Py_TYPE(obj)->tp_call != NULL)
#else
#define __Pyx_PyCallable_Check(obj) PyCallable_Check(obj)
#endif
@@ -936,6 +1408,37 @@
return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
}
+/////////////// PySetContains.proto ///////////////
+
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq); /* proto */
+
+/////////////// PySetContains ///////////////
+//@requires: Builtins.c::pyfrozenset_new
+
+static int __Pyx_PySet_ContainsUnhashable(PyObject *set, PyObject *key) {
+ int result = -1;
+ if (PySet_Check(key) && PyErr_ExceptionMatches(PyExc_TypeError)) {
+ /* Convert key to frozenset */
+ PyObject *tmpkey;
+ PyErr_Clear();
+ tmpkey = __Pyx_PyFrozenSet_New(key);
+ if (tmpkey != NULL) {
+ result = PySet_Contains(set, tmpkey);
+ Py_DECREF(tmpkey);
+ }
+ }
+ return result;
+}
+
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq) {
+ int result = PySet_Contains(set, key);
+
+ if (unlikely(result < 0)) {
+ result = __Pyx_PySet_ContainsUnhashable(set, key);
+ }
+ return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
+}
+
/////////////// PySequenceContains.proto ///////////////
static CYTHON_INLINE int __Pyx_PySequence_ContainsTF(PyObject* item, PyObject* seq, int eq) {
@@ -954,12 +1457,12 @@
static PyObject *__Pyx_GetBuiltinName(PyObject *name); /*proto*/
/////////////// GetBuiltinName ///////////////
-//@requires: PyObjectGetAttrStr
+//@requires: PyObjectGetAttrStrNoError
//@substitute: naming
static PyObject *__Pyx_GetBuiltinName(PyObject *name) {
- PyObject* result = __Pyx_PyObject_GetAttrStr($builtins_cname, name);
- if (unlikely(!result)) {
+ PyObject* result = __Pyx_PyObject_GetAttrStrNoError($builtins_cname, name);
+ if (unlikely(!result) && !PyErr_Occurred()) {
PyErr_Format(PyExc_NameError,
#if PY_MAJOR_VERSION >= 3
"name '%U' is not defined", name);
@@ -972,43 +1475,144 @@
/////////////// GetNameInClass.proto ///////////////
-static PyObject *__Pyx_GetNameInClass(PyObject *nmspace, PyObject *name); /*proto*/
+#define __Pyx_GetNameInClass(var, nmspace, name) (var) = __Pyx__GetNameInClass(nmspace, name)
+static PyObject *__Pyx__GetNameInClass(PyObject *nmspace, PyObject *name); /*proto*/
/////////////// GetNameInClass ///////////////
-//@requires: PyObjectGetAttrStr
//@requires: GetModuleGlobalName
-static PyObject *__Pyx_GetNameInClass(PyObject *nmspace, PyObject *name) {
+static PyObject *__Pyx__GetNameInClass(PyObject *nmspace, PyObject *name) {
PyObject *result;
- result = __Pyx_PyObject_GetAttrStr(nmspace, name);
- if (!result)
- result = __Pyx_GetModuleGlobalName(name);
- return result;
+ PyObject *dict;
+ assert(PyType_Check(nmspace));
+#if CYTHON_USE_TYPE_SLOTS
+ dict = ((PyTypeObject*)nmspace)->tp_dict;
+ Py_XINCREF(dict);
+#else
+ dict = PyObject_GetAttr(nmspace, PYIDENT("__dict__"));
+#endif
+ if (likely(dict)) {
+ result = PyObject_GetItem(dict, name);
+ Py_DECREF(dict);
+ if (result) {
+ return result;
+ }
+ }
+ PyErr_Clear();
+ __Pyx_GetModuleGlobalNameUncached(result, name);
+ return result;
+}
+
+
+/////////////// SetNameInClass.proto ///////////////
+
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030500A1
+// Identifier names are always interned and have a pre-calculated hash value.
+#define __Pyx_SetNameInClass(ns, name, value) \
+ (likely(PyDict_CheckExact(ns)) ? _PyDict_SetItem_KnownHash(ns, name, value, ((PyASCIIObject *) name)->hash) : PyObject_SetItem(ns, name, value))
+#elif CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_SetNameInClass(ns, name, value) \
+ (likely(PyDict_CheckExact(ns)) ? PyDict_SetItem(ns, name, value) : PyObject_SetItem(ns, name, value))
+#else
+#define __Pyx_SetNameInClass(ns, name, value) PyObject_SetItem(ns, name, value)
+#endif
+
+/////////////// SetNewInClass.proto ///////////////
+
+static int __Pyx_SetNewInClass(PyObject *ns, PyObject *name, PyObject *value);
+
+/////////////// SetNewInClass ///////////////
+//@requires: SetNameInClass
+
+// Special-case setting __new__: if it's a Cython function, wrap it in a
+// staticmethod. This is similar to what Python does for a Python function
+// called __new__.
+static int __Pyx_SetNewInClass(PyObject *ns, PyObject *name, PyObject *value) {
+#ifdef __Pyx_CyFunction_USED
+ int ret;
+ if (__Pyx_CyFunction_Check(value)) {
+ PyObject *staticnew = PyStaticMethod_New(value);
+ if (unlikely(!staticnew)) return -1;
+ ret = __Pyx_SetNameInClass(ns, name, staticnew);
+ Py_DECREF(staticnew);
+ return ret;
+ }
+#endif
+ return __Pyx_SetNameInClass(ns, name, value);
}
+
/////////////// GetModuleGlobalName.proto ///////////////
+//@requires: PyDictVersioning
+//@substitute: naming
+
+#if CYTHON_USE_DICT_VERSIONS
+#define __Pyx_GetModuleGlobalName(var, name) { \
+ static PY_UINT64_T __pyx_dict_version = 0; \
+ static PyObject *__pyx_dict_cached_value = NULL; \
+ (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION($moddict_cname))) ? \
+ (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) : \
+ __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value); \
+}
+#define __Pyx_GetModuleGlobalNameUncached(var, name) { \
+ PY_UINT64_T __pyx_dict_version; \
+ PyObject *__pyx_dict_cached_value; \
+ (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value); \
+}
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value); /*proto*/
+#else
+#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); /*proto*/
+#endif
-static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name); /*proto*/
/////////////// GetModuleGlobalName ///////////////
//@requires: GetBuiltinName
//@substitute: naming
-static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name) {
+#if CYTHON_USE_DICT_VERSIONS
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value)
+#else
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name)
+#endif
+{
PyObject *result;
+// FIXME: clean up the macro guard order here: limited API first, then borrowed refs, then cpython
#if !CYTHON_AVOID_BORROWED_REFS
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030500A1
+ // Identifier names are always interned and have a pre-calculated hash value.
+ result = _PyDict_GetItem_KnownHash($moddict_cname, name, ((PyASCIIObject *) name)->hash);
+ __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return __Pyx_NewRef(result);
+ } else if (unlikely(PyErr_Occurred())) {
+ return NULL;
+ }
+#elif CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(!$module_cname)) {
+ return NULL;
+ }
+ result = PyObject_GetAttr($module_cname, name);
+ if (likely(result)) {
+ return result;
+ }
+#else
result = PyDict_GetItem($moddict_cname, name);
+ __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version)
if (likely(result)) {
- Py_INCREF(result);
- } else {
+ return __Pyx_NewRef(result);
+ }
+#endif
#else
result = PyObject_GetItem($moddict_cname, name);
- if (!result) {
- PyErr_Clear();
-#endif
- result = __Pyx_GetBuiltinName(name);
+ __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return __Pyx_NewRef(result);
}
- return result;
+ PyErr_Clear();
+#endif
+ return __Pyx_GetBuiltinName(name);
}
//////////////////// GetAttr.proto ////////////////////
@@ -1019,7 +1623,7 @@
//@requires: PyObjectGetAttrStr
static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *o, PyObject *n) {
-#if CYTHON_COMPILING_IN_CPYTHON
+#if CYTHON_USE_TYPE_SLOTS
#if PY_MAJOR_VERSION >= 3
if (likely(PyUnicode_Check(n)))
#else
@@ -1030,17 +1634,31 @@
return PyObject_GetAttr(o, n);
}
+
/////////////// PyObjectLookupSpecial.proto ///////////////
+
+#if CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS
+#define __Pyx_PyObject_LookupSpecialNoError(obj, attr_name) __Pyx__PyObject_LookupSpecial(obj, attr_name, 0)
+#define __Pyx_PyObject_LookupSpecial(obj, attr_name) __Pyx__PyObject_LookupSpecial(obj, attr_name, 1)
+
+static CYTHON_INLINE PyObject* __Pyx__PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name, int with_error); /*proto*/
+
+#else
+#define __Pyx_PyObject_LookupSpecialNoError(o,n) __Pyx_PyObject_GetAttrStrNoError(o,n)
+#define __Pyx_PyObject_LookupSpecial(o,n) __Pyx_PyObject_GetAttrStr(o,n)
+#endif
+
+/////////////// PyObjectLookupSpecial ///////////////
//@requires: PyObjectGetAttrStr
+//@requires: PyObjectGetAttrStrNoError
-#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070000
-// looks like calling _PyType_Lookup() isn't safe in Py<=2.6/3.1
-static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name) {
+#if CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx__PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name, int with_error) {
PyObject *res;
PyTypeObject *tp = Py_TYPE(obj);
#if PY_MAJOR_VERSION < 3
if (unlikely(PyInstance_Check(obj)))
- return __Pyx_PyObject_GetAttrStr(obj, attr_name);
+ return with_error ? __Pyx_PyObject_GetAttrStr(obj, attr_name) : __Pyx_PyObject_GetAttrStrNoError(obj, attr_name);
#endif
// adapted from CPython's special_lookup() in ceval.c
res = _PyType_Lookup(tp, attr_name);
@@ -1051,18 +1669,146 @@
} else {
res = f(res, obj, (PyObject *)tp);
}
- } else {
+ } else if (with_error) {
PyErr_SetObject(PyExc_AttributeError, attr_name);
}
return res;
}
+#endif
+
+
+/////////////// PyObject_GenericGetAttrNoDict.proto ///////////////
+
+// Setting "tp_getattro" to anything but "PyObject_GenericGetAttr" disables fast method calls in Py3.7.
+#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name);
#else
-#define __Pyx_PyObject_LookupSpecial(o,n) __Pyx_PyObject_GetAttrStr(o,n)
+// No-args macro to allow function pointer assignment.
+#define __Pyx_PyObject_GenericGetAttrNoDict PyObject_GenericGetAttr
#endif
+/////////////// PyObject_GenericGetAttrNoDict ///////////////
+
+#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000
+
+static PyObject *__Pyx_RaiseGenericGetAttributeError(PyTypeObject *tp, PyObject *attr_name) {
+ __Pyx_TypeName type_name = __Pyx_PyType_GetName(tp);
+ PyErr_Format(PyExc_AttributeError,
+#if PY_MAJOR_VERSION >= 3
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'",
+ type_name, attr_name);
+#else
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'",
+ type_name, PyString_AS_STRING(attr_name));
+#endif
+ __Pyx_DECREF_TypeName(type_name);
+ return NULL;
+}
+
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name) {
+ // Copied and adapted from _PyObject_GenericGetAttrWithDict() in CPython 3.6/3.7.
+ // To be used in the "tp_getattro" slot of extension types that have no instance dict and cannot be subclassed.
+ PyObject *descr;
+ PyTypeObject *tp = Py_TYPE(obj);
+
+ if (unlikely(!PyString_Check(attr_name))) {
+ return PyObject_GenericGetAttr(obj, attr_name);
+ }
+
+ assert(!tp->tp_dictoffset);
+ descr = _PyType_Lookup(tp, attr_name);
+ if (unlikely(!descr)) {
+ return __Pyx_RaiseGenericGetAttributeError(tp, attr_name);
+ }
+
+ Py_INCREF(descr);
+
+ #if PY_MAJOR_VERSION < 3
+ if (likely(PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS)))
+ #endif
+ {
+ descrgetfunc f = Py_TYPE(descr)->tp_descr_get;
+ // Optimise for the non-descriptor case because it is faster.
+ if (unlikely(f)) {
+ PyObject *res = f(descr, obj, (PyObject *)tp);
+ Py_DECREF(descr);
+ return res;
+ }
+ }
+ return descr;
+}
+#endif
+
+
+/////////////// PyObject_GenericGetAttr.proto ///////////////
+
+// Setting "tp_getattro" to anything but "PyObject_GenericGetAttr" disables fast method calls in Py3.7.
+#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000
+static PyObject* __Pyx_PyObject_GenericGetAttr(PyObject* obj, PyObject* attr_name);
+#else
+// No-args macro to allow function pointer assignment.
+#define __Pyx_PyObject_GenericGetAttr PyObject_GenericGetAttr
+#endif
+
+/////////////// PyObject_GenericGetAttr ///////////////
+//@requires: PyObject_GenericGetAttrNoDict
+
+#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000
+static PyObject* __Pyx_PyObject_GenericGetAttr(PyObject* obj, PyObject* attr_name) {
+ if (unlikely(Py_TYPE(obj)->tp_dictoffset)) {
+ return PyObject_GenericGetAttr(obj, attr_name);
+ }
+ return __Pyx_PyObject_GenericGetAttrNoDict(obj, attr_name);
+}
+#endif
+
+
+/////////////// PyObjectGetAttrStrNoError.proto ///////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name);/*proto*/
+
+/////////////// PyObjectGetAttrStrNoError ///////////////
+//@requires: PyObjectGetAttrStr
+//@requires: Exceptions.c::PyThreadStateGet
+//@requires: Exceptions.c::PyErrFetchRestore
+//@requires: Exceptions.c::PyErrExceptionMatches
+
+static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError)))
+ __Pyx_PyErr_Clear();
+}
+
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) {
+ PyObject *result;
+#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS && PY_VERSION_HEX >= 0x030700B1
+ // _PyObject_GenericGetAttrWithDict() in CPython 3.7+ can avoid raising the AttributeError.
+ // See https://bugs.python.org/issue32544
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) {
+ return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1);
+ }
+#endif
+ result = __Pyx_PyObject_GetAttrStr(obj, attr_name);
+ if (unlikely(!result)) {
+ __Pyx_PyObject_GetAttrStr_ClearAttributeError();
+ }
+ return result;
+}
+
+
/////////////// PyObjectGetAttrStr.proto ///////////////
#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);/*proto*/
+#else
+#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n)
+#endif
+
+/////////////// PyObjectGetAttrStr ///////////////
+
+#if CYTHON_USE_TYPE_SLOTS
static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) {
PyTypeObject* tp = Py_TYPE(obj);
if (likely(tp->tp_getattro))
@@ -1073,14 +1819,22 @@
#endif
return PyObject_GetAttr(obj, attr_name);
}
-#else
-#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n)
#endif
+
/////////////// PyObjectSetAttrStr.proto ///////////////
#if CYTHON_USE_TYPE_SLOTS
-#define __Pyx_PyObject_DelAttrStr(o,n) __Pyx_PyObject_SetAttrStr(o,n,NULL)
+#define __Pyx_PyObject_DelAttrStr(o,n) __Pyx_PyObject_SetAttrStr(o, n, NULL)
+static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr_name, PyObject* value);/*proto*/
+#else
+#define __Pyx_PyObject_DelAttrStr(o,n) PyObject_DelAttr(o,n)
+#define __Pyx_PyObject_SetAttrStr(o,n,v) PyObject_SetAttr(o,n,v)
+#endif
+
+/////////////// PyObjectSetAttrStr ///////////////
+
+#if CYTHON_USE_TYPE_SLOTS
static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr_name, PyObject* value) {
PyTypeObject* tp = Py_TYPE(obj);
if (likely(tp->tp_setattro))
@@ -1091,10 +1845,130 @@
#endif
return PyObject_SetAttr(obj, attr_name, value);
}
+#endif
+
+
+/////////////// PyObjectGetMethod.proto ///////////////
+
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);/*proto*/
+
+/////////////// PyObjectGetMethod ///////////////
+//@requires: PyObjectGetAttrStr
+
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) {
+ PyObject *attr;
+#if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP
+ __Pyx_TypeName type_name;
+ // Copied from _PyObject_GetMethod() in CPython 3.7
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyObject *descr;
+ descrgetfunc f = NULL;
+ PyObject **dictptr, *dict;
+ int meth_found = 0;
+
+ assert (*method == NULL);
+
+ if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) {
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+ }
+ if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) {
+ return 0;
+ }
+
+ descr = _PyType_Lookup(tp, name);
+ if (likely(descr != NULL)) {
+ Py_INCREF(descr);
+#if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR
+ if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR))
+#elif PY_MAJOR_VERSION >= 3
+ // Repeating the condition below accommodates for MSVC's inability to test macros inside of macro expansions.
+ #ifdef __Pyx_CyFunction_USED
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr)))
+ #else
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type)))
+ #endif
#else
-#define __Pyx_PyObject_DelAttrStr(o,n) PyObject_DelAttr(o,n)
-#define __Pyx_PyObject_SetAttrStr(o,n,v) PyObject_SetAttr(o,n,v)
+ // "PyMethodDescr_Type" is not part of the C-API in Py2.
+ #ifdef __Pyx_CyFunction_USED
+ if (likely(PyFunction_Check(descr) || __Pyx_CyFunction_Check(descr)))
+ #else
+ if (likely(PyFunction_Check(descr)))
+ #endif
+#endif
+ {
+ meth_found = 1;
+ } else {
+ f = Py_TYPE(descr)->tp_descr_get;
+ if (f != NULL && PyDescr_IsData(descr)) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+ }
+ }
+
+ dictptr = _PyObject_GetDictPtr(obj);
+ if (dictptr != NULL && (dict = *dictptr) != NULL) {
+ Py_INCREF(dict);
+ attr = __Pyx_PyDict_GetItemStr(dict, name);
+ if (attr != NULL) {
+ Py_INCREF(attr);
+ Py_DECREF(dict);
+ Py_XDECREF(descr);
+ goto try_unpack;
+ }
+ Py_DECREF(dict);
+ }
+
+ if (meth_found) {
+ *method = descr;
+ return 1;
+ }
+
+ if (f != NULL) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+
+ if (likely(descr != NULL)) {
+ *method = descr;
+ return 0;
+ }
+
+ type_name = __Pyx_PyType_GetName(tp);
+ PyErr_Format(PyExc_AttributeError,
+#if PY_MAJOR_VERSION >= 3
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'",
+ type_name, name);
+#else
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'",
+ type_name, PyString_AS_STRING(name));
+#endif
+ __Pyx_DECREF_TypeName(type_name);
+ return 0;
+
+// Generic fallback implementation using normal attribute lookup.
+#else
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+#endif
+
+try_unpack:
+#if CYTHON_UNPACK_METHODS
+ // Even if we failed to avoid creating a bound method object, it's still worth unpacking it now, if possible.
+ if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) {
+ PyObject *function = PyMethod_GET_FUNCTION(attr);
+ Py_INCREF(function);
+ Py_DECREF(attr);
+ *method = function;
+ return 1;
+ }
#endif
+ *method = attr;
+ return 0;
+}
/////////////// UnpackUnboundCMethod.proto ///////////////
@@ -1120,13 +1994,13 @@
target->method = method;
#if CYTHON_COMPILING_IN_CPYTHON
#if PY_MAJOR_VERSION >= 3
- // method dscriptor type isn't exported in Py2.x, cannot easily check the type there
- if (likely(PyObject_TypeCheck(method, &PyMethodDescr_Type)))
+ // method descriptor type isn't exported in Py2.x, cannot easily check the type there
+ if (likely(__Pyx_TypeCheck(method, &PyMethodDescr_Type)))
#endif
{
PyMethodDescrObject *descr = (PyMethodDescrObject*) method;
target->func = descr->d_method->ml_meth;
- target->flag = descr->d_method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
+ target->flag = descr->d_method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_STACKLESS);
}
#endif
return 0;
@@ -1138,13 +2012,19 @@
static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self); /*proto*/
#if CYTHON_COMPILING_IN_CPYTHON
+// FASTCALL methods receive "&empty_tuple" as simple "PyObject[0]*"
#define __Pyx_CallUnboundCMethod0(cfunc, self) \
- ((likely((cfunc)->func)) ? \
+ (likely((cfunc)->func) ? \
(likely((cfunc)->flag == METH_NOARGS) ? (*((cfunc)->func))(self, NULL) : \
- (likely((cfunc)->flag == (METH_VARARGS | METH_KEYWORDS)) ? ((*(PyCFunctionWithKeywords)(cfunc)->func)(self, $empty_tuple, NULL)) : \
- ((cfunc)->flag == METH_VARARGS ? (*((cfunc)->func))(self, $empty_tuple) : \
- (PY_VERSION_HEX >= 0x030600B1 && (cfunc)->flag == METH_FASTCALL ? (*(__Pyx_PyCFunctionFast)(cfunc)->func)(self, &PyTuple_GET_ITEM($empty_tuple, 0), 0, NULL) : \
- __Pyx__CallUnboundCMethod0(cfunc, self))))) : \
+ (PY_VERSION_HEX >= 0x030600B1 && likely((cfunc)->flag == METH_FASTCALL) ? \
+ (PY_VERSION_HEX >= 0x030700A0 ? \
+ (*(__Pyx_PyCFunctionFast)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0) : \
+ (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0, NULL)) : \
+ (PY_VERSION_HEX >= 0x030700A0 && (cfunc)->flag == (METH_FASTCALL | METH_KEYWORDS) ? \
+ (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0, NULL) : \
+ (likely((cfunc)->flag == (METH_VARARGS | METH_KEYWORDS)) ? ((*(PyCFunctionWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, $empty_tuple, NULL)) : \
+ ((cfunc)->flag == METH_VARARGS ? (*((cfunc)->func))(self, $empty_tuple) : \
+ __Pyx__CallUnboundCMethod0(cfunc, self)))))) : \
__Pyx__CallUnboundCMethod0(cfunc, self))
#else
#define __Pyx_CallUnboundCMethod0(cfunc, self) __Pyx__CallUnboundCMethod0(cfunc, self)
@@ -1175,14 +2055,10 @@
/////////////// CallUnboundCMethod1.proto ///////////////
-static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg); /*proto*/
+static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg);/*proto*/
#if CYTHON_COMPILING_IN_CPYTHON
-#define __Pyx_CallUnboundCMethod1(cfunc, self, arg) \
- ((likely((cfunc)->func && (cfunc)->flag == METH_O)) ? (*((cfunc)->func))(self, arg) : \
- ((PY_VERSION_HEX >= 0x030600B1 && (cfunc)->func && (cfunc)->flag == METH_FASTCALL) ? \
- (*(__Pyx_PyCFunctionFast)(cfunc)->func)(self, &arg, 1, NULL) : \
- __Pyx__CallUnboundCMethod1(cfunc, self, arg)))
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg);/*proto*/
#else
#define __Pyx_CallUnboundCMethod1(cfunc, self, arg) __Pyx__CallUnboundCMethod1(cfunc, self, arg)
#endif
@@ -1191,9 +2067,30 @@
//@requires: UnpackUnboundCMethod
//@requires: PyObjectCall
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg) {
+ if (likely(cfunc->func)) {
+ int flag = cfunc->flag;
+ // Not using #ifdefs for PY_VERSION_HEX to avoid C compiler warnings about unused functions.
+ if (flag == METH_O) {
+ return (*(cfunc->func))(self, arg);
+ } else if ((PY_VERSION_HEX >= 0x030600B1) && flag == METH_FASTCALL) {
+ if ((PY_VERSION_HEX >= 0x030700A0)) {
+ return (*(__Pyx_PyCFunctionFast)(void*)(PyCFunction)cfunc->func)(self, &arg, 1);
+ } else {
+ return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, &arg, 1, NULL);
+ }
+ } else if ((PY_VERSION_HEX >= 0x030700A0) && flag == (METH_FASTCALL | METH_KEYWORDS)) {
+ return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, &arg, 1, NULL);
+ }
+ }
+ return __Pyx__CallUnboundCMethod1(cfunc, self, arg);
+}
+#endif
+
static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg){
PyObject *args, *result = NULL;
- if (unlikely(!cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+ if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
#if CYTHON_COMPILING_IN_CPYTHON
if (cfunc->func && (cfunc->flag & METH_VARARGS)) {
args = PyTuple_New(1);
@@ -1201,7 +2098,7 @@
Py_INCREF(arg);
PyTuple_SET_ITEM(args, 0, arg);
if (cfunc->flag & METH_KEYWORDS)
- result = (*(PyCFunctionWithKeywords)cfunc->func)(self, args, NULL);
+ result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL);
else
result = (*cfunc->func)(self, args);
} else {
@@ -1224,168 +2121,228 @@
}
-/////////////// PyObjectCallMethod0.proto ///////////////
+/////////////// CallUnboundCMethod2.proto ///////////////
-static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); /*proto*/
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2); /*proto*/
-/////////////// PyObjectCallMethod0 ///////////////
-//@requires: PyObjectGetAttrStr
-//@requires: PyObjectCallOneArg
-//@requires: PyObjectCallNoArg
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030600B1
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2); /*proto*/
+#else
+#define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2)
+#endif
-static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
- PyObject *method, *result = NULL;
- method = __Pyx_PyObject_GetAttrStr(obj, method_name);
- if (unlikely(!method)) goto bad;
-#if CYTHON_UNPACK_METHODS
- if (likely(PyMethod_Check(method))) {
- PyObject *self = PyMethod_GET_SELF(method);
- if (likely(self)) {
- PyObject *function = PyMethod_GET_FUNCTION(method);
- result = __Pyx_PyObject_CallOneArg(function, self);
- Py_DECREF(method);
- return result;
+/////////////// CallUnboundCMethod2 ///////////////
+//@requires: UnpackUnboundCMethod
+//@requires: PyObjectCall
+
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030600B1
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2) {
+ if (likely(cfunc->func)) {
+ PyObject *args[2] = {arg1, arg2};
+ if (cfunc->flag == METH_FASTCALL) {
+ #if PY_VERSION_HEX >= 0x030700A0
+ return (*(__Pyx_PyCFunctionFast)(void*)(PyCFunction)cfunc->func)(self, args, 2);
+ #else
+ return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, 2, NULL);
+ #endif
}
+ #if PY_VERSION_HEX >= 0x030700A0
+ if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS))
+ return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, 2, NULL);
+ #endif
}
+ return __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2);
+}
+#endif
+
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2){
+ PyObject *args, *result = NULL;
+ if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (cfunc->func && (cfunc->flag & METH_VARARGS)) {
+ args = PyTuple_New(2);
+ if (unlikely(!args)) goto bad;
+ Py_INCREF(arg1);
+ PyTuple_SET_ITEM(args, 0, arg1);
+ Py_INCREF(arg2);
+ PyTuple_SET_ITEM(args, 1, arg2);
+ if (cfunc->flag & METH_KEYWORDS)
+ result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL);
+ else
+ result = (*cfunc->func)(self, args);
+ } else {
+ args = PyTuple_New(3);
+ if (unlikely(!args)) goto bad;
+ Py_INCREF(self);
+ PyTuple_SET_ITEM(args, 0, self);
+ Py_INCREF(arg1);
+ PyTuple_SET_ITEM(args, 1, arg1);
+ Py_INCREF(arg2);
+ PyTuple_SET_ITEM(args, 2, arg2);
+ result = __Pyx_PyObject_Call(cfunc->method, args, NULL);
+ }
+#else
+ args = PyTuple_Pack(3, self, arg1, arg2);
+ if (unlikely(!args)) goto bad;
+ result = __Pyx_PyObject_Call(cfunc->method, args, NULL);
#endif
- result = __Pyx_PyObject_CallNoArg(method);
- Py_DECREF(method);
bad:
+ Py_XDECREF(args);
return result;
}
-/////////////// PyObjectCallMethod1.proto ///////////////
+/////////////// PyObjectFastCall.proto ///////////////
-static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg); /*proto*/
+#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL)
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /*proto*/
-/////////////// PyObjectCallMethod1 ///////////////
-//@requires: PyObjectGetAttrStr
-//@requires: PyObjectCallOneArg
+/////////////// PyObjectFastCall ///////////////
+//@requires: PyObjectCall
//@requires: PyFunctionFastCall
-//@requires: PyCFunctionFastCall
+//@requires: PyObjectCallMethO
+//@substitute: naming
-static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
- PyObject *method, *result = NULL;
- method = __Pyx_PyObject_GetAttrStr(obj, method_name);
- if (unlikely(!method)) goto done;
-#if CYTHON_UNPACK_METHODS
- if (likely(PyMethod_Check(method))) {
- PyObject *self = PyMethod_GET_SELF(method);
- if (likely(self)) {
- PyObject *args;
- PyObject *function = PyMethod_GET_FUNCTION(method);
- #if CYTHON_FAST_PYCALL
- if (PyFunction_Check(function)) {
- PyObject *args[2] = {self, arg};
- result = __Pyx_PyFunction_FastCall(function, args, 2);
- goto done;
+static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) {
+ PyObject *argstuple;
+ PyObject *result;
+ size_t i;
+
+ argstuple = PyTuple_New((Py_ssize_t)nargs);
+ if (unlikely(!argstuple)) return NULL;
+ for (i = 0; i < nargs; i++) {
+ Py_INCREF(args[i]);
+ PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]);
+ }
+ result = __Pyx_PyObject_Call(func, argstuple, kwargs);
+ Py_DECREF(argstuple);
+ return result;
+}
+
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) {
+ // Special fast paths for 0 and 1 arguments
+ // NOTE: in many cases, this is called with a constant value for nargs
+ // which is known at compile-time. So the branches below will typically
+ // be optimized away.
+ Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs);
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (nargs == 0 && kwargs == NULL) {
+#ifdef __Pyx_CyFunction_USED
+ if (__Pyx_IsCyOrPyCFunction(func))
+#else
+ if (PyCFunction_Check(func))
+#endif
+ {
+ if (likely(PyCFunction_GET_FLAGS(func) & METH_NOARGS)) {
+ return __Pyx_PyObject_CallMethO(func, NULL);
}
- #endif
- #if CYTHON_FAST_PYCCALL
- if (__Pyx_PyFastCFunction_Check(function)) {
- PyObject *args[2] = {self, arg};
- result = __Pyx_PyCFunction_FastCall(function, args, 2);
- goto done;
+ }
+ }
+ else if (nargs == 1 && kwargs == NULL) {
+ if (PyCFunction_Check(func))
+ {
+ if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
+ return __Pyx_PyObject_CallMethO(func, args[0]);
}
- #endif
- args = PyTuple_New(2);
- if (unlikely(!args)) goto done;
- Py_INCREF(self);
- PyTuple_SET_ITEM(args, 0, self);
- Py_INCREF(arg);
- PyTuple_SET_ITEM(args, 1, arg);
- Py_INCREF(function);
- Py_DECREF(method); method = NULL;
- result = __Pyx_PyObject_Call(function, args, NULL);
- Py_DECREF(args);
- Py_DECREF(function);
- return result;
}
}
#endif
- result = __Pyx_PyObject_CallOneArg(method, arg);
-done:
- Py_XDECREF(method);
- return result;
+
+ #if PY_VERSION_HEX < 0x030800B1
+ #if CYTHON_FAST_PYCCALL
+ if (PyCFunction_Check(func)) {
+ if (kwargs) {
+ return _PyCFunction_FastCallDict(func, args, nargs, kwargs);
+ } else {
+ return _PyCFunction_FastCallKeywords(func, args, nargs, NULL);
+ }
+ }
+ #if PY_VERSION_HEX >= 0x030700A1
+ if (!kwargs && __Pyx_IS_TYPE(func, &PyMethodDescr_Type)) {
+ return _PyMethodDescr_FastCallKeywords(func, args, nargs, NULL);
+ }
+ #endif
+ #endif
+ #if CYTHON_FAST_PYCALL
+ if (PyFunction_Check(func)) {
+ return __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs);
+ }
+ #endif
+ #endif
+
+ #if CYTHON_VECTORCALL
+ vectorcallfunc f = _PyVectorcall_Function(func);
+ if (f) {
+ return f(func, args, (size_t)nargs, kwargs);
+ }
+ #elif defined(__Pyx_CyFunction_USED) && CYTHON_BACKPORT_VECTORCALL
+ // exclude fused functions for now
+ if (__Pyx_CyFunction_CheckExact(func)) {
+ __pyx_vectorcallfunc f = __Pyx_CyFunction_func_vectorcall(func);
+ if (f) return f(func, args, (size_t)nargs, kwargs);
+ }
+ #endif
+
+ if (nargs == 0) {
+ return __Pyx_PyObject_Call(func, $empty_tuple, kwargs);
+ }
+ return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs);
}
-/////////////// PyObjectCallMethod2.proto ///////////////
+/////////////// PyObjectCallMethod0.proto ///////////////
-static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name, PyObject* arg1, PyObject* arg2); /*proto*/
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); /*proto*/
-/////////////// PyObjectCallMethod2 ///////////////
-//@requires: PyObjectGetAttrStr
-//@requires: PyObjectCall
-//@requires: PyFunctionFastCall
-//@requires: PyCFunctionFastCall
+/////////////// PyObjectCallMethod0 ///////////////
+//@requires: PyObjectGetMethod
+//@requires: PyObjectCallOneArg
+//@requires: PyObjectCallNoArg
-static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name, PyObject* arg1, PyObject* arg2) {
- PyObject *args, *method, *result = NULL;
- method = __Pyx_PyObject_GetAttrStr(obj, method_name);
- if (unlikely(!method)) return NULL;
-#if CYTHON_UNPACK_METHODS
- if (likely(PyMethod_Check(method)) && likely(PyMethod_GET_SELF(method))) {
- PyObject *self, *function;
- self = PyMethod_GET_SELF(method);
- function = PyMethod_GET_FUNCTION(method);
- #if CYTHON_FAST_PYCALL
- if (PyFunction_Check(function)) {
- PyObject *args[3] = {self, arg1, arg2};
- result = __Pyx_PyFunction_FastCall(function, args, 3);
- goto done;
- }
- #endif
- #if CYTHON_FAST_PYCCALL
- if (__Pyx_PyFastCFunction_Check(function)) {
- PyObject *args[3] = {self, arg1, arg2};
- result = __Pyx_PyFunction_FastCall(function, args, 3);
- goto done;
- }
- #endif
- args = PyTuple_New(3);
- if (unlikely(!args)) goto done;
- Py_INCREF(self);
- PyTuple_SET_ITEM(args, 0, self);
- Py_INCREF(arg1);
- PyTuple_SET_ITEM(args, 1, arg1);
- Py_INCREF(arg2);
- PyTuple_SET_ITEM(args, 2, arg2);
- Py_INCREF(function);
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
+ PyObject *method = NULL, *result = NULL;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_CallOneArg(method, obj);
Py_DECREF(method);
- method = function;
- } else
-#endif
-#if CYTHON_FAST_PYCALL
- if (PyFunction_Check(method)) {
- PyObject *args[2] = {arg1, arg2};
- result = __Pyx_PyFunction_FastCall(method, args, 2);
- goto done;
- } else
-#endif
-#if CYTHON_FAST_PYCCALL
- if (__Pyx_PyFastCFunction_Check(method)) {
- PyObject *args[2] = {arg1, arg2};
- result = __Pyx_PyCFunction_FastCall(method, args, 2);
- goto done;
- } else
-#endif
- {
- args = PyTuple_New(2);
- if (unlikely(!args)) goto done;
- Py_INCREF(arg1);
- PyTuple_SET_ITEM(args, 0, arg1);
- Py_INCREF(arg2);
- PyTuple_SET_ITEM(args, 1, arg2);
+ return result;
}
- result = __Pyx_PyObject_Call(method, args, NULL);
- Py_DECREF(args);
-done:
+ if (unlikely(!method)) goto bad;
+ result = __Pyx_PyObject_CallNoArg(method);
+ Py_DECREF(method);
+bad:
+ return result;
+}
+
+
+/////////////// PyObjectCallMethod1.proto ///////////////
+
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg); /*proto*/
+
+/////////////// PyObjectCallMethod1 ///////////////
+//@requires: PyObjectGetMethod
+//@requires: PyObjectCallOneArg
+//@requires: PyObjectCall2Args
+
+static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
+ // Separate function to avoid excessive inlining.
+ PyObject *result = __Pyx_PyObject_CallOneArg(method, arg);
Py_DECREF(method);
return result;
}
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
+ PyObject *method = NULL, *result;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_Call2Args(method, obj, arg);
+ Py_DECREF(method);
+ return result;
+ }
+ if (unlikely(!method)) return NULL;
+ return __Pyx__PyObject_CallMethod1(method, arg);
+}
+
/////////////// tp_new.proto ///////////////
@@ -1408,7 +2365,7 @@
#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) {
PyObject *result;
- ternaryfunc call = func->ob_type->tp_call;
+ ternaryfunc call = Py_TYPE(func)->tp_call;
if (unlikely(!call))
return PyObject_Call(func, arg, kw);
@@ -1458,27 +2415,72 @@
/////////////// PyFunctionFastCall.proto ///////////////
#if CYTHON_FAST_PYCALL
+
+#if !CYTHON_VECTORCALL
#define __Pyx_PyFunction_FastCall(func, args, nargs) \
__Pyx_PyFunction_FastCallDict((func), (args), (nargs), NULL)
-// let's assume that the non-public C-API function might still change during the 3.6 beta phase
-#if 1 || PY_VERSION_HEX < 0x030600B1
-static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs);
-#else
-#define __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs) _PyFunction_FastCallDict(func, args, nargs, kwargs)
+static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs);
#endif
+
+// Backport from Python 3
+// Assert a build-time dependency, as an expression.
+// Your compile will fail if the condition isn't true, or can't be evaluated
+// by the compiler. This can be used in an expression: its value is 0.
+// Example:
+// #define foo_to_char(foo) \
+// ((char *)(foo) \
+// + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0))
+//
+// Written by Rusty Russell, public domain, https://ccodearchive.net/
+#define __Pyx_BUILD_ASSERT_EXPR(cond) \
+ (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#ifndef Py_MEMBER_SIZE
+// Get the size of a structure member in bytes
+#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
+#endif
+
+#if !CYTHON_VECTORCALL
+#if PY_VERSION_HEX >= 0x03080000
+ #include "frameobject.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+ #define __Pxy_PyFrame_Initialize_Offsets()
+ #define __Pyx_PyFrame_GetLocalsplus(frame) ((frame)->f_localsplus)
+#else
+ // Initialised by module init code.
+ static size_t __pyx_pyframe_localsplus_offset = 0;
+
+ #include "frameobject.h"
+ // This is the long runtime version of
+ // #define __Pyx_PyFrame_GetLocalsplus(frame) ((frame)->f_localsplus)
+ // offsetof(PyFrameObject, f_localsplus) differs between regular C-Python and Stackless Python < 3.8.
+ // Therefore the offset is computed at run time from PyFrame_type.tp_basicsize. That is feasible,
+ // because f_localsplus is the last field of PyFrameObject (checked by Py_BUILD_ASSERT_EXPR below).
+ #define __Pxy_PyFrame_Initialize_Offsets() \
+ ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \
+ (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
+ #define __Pyx_PyFrame_GetLocalsplus(frame) \
+ (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))
#endif
+#endif /* !CYTHON_VECTORCALL */
+
+#endif /* CYTHON_FAST_PYCALL */
+
/////////////// PyFunctionFastCall ///////////////
// copied from CPython 3.6 ceval.c
-#if CYTHON_FAST_PYCALL
-#include "frameobject.h"
-
+#if CYTHON_FAST_PYCALL && !CYTHON_VECTORCALL
static PyObject* __Pyx_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na,
PyObject *globals) {
PyFrameObject *f;
- PyThreadState *tstate = PyThreadState_GET();
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
PyObject **fastlocals;
Py_ssize_t i;
PyObject *result;
@@ -1494,7 +2496,7 @@
return NULL;
}
- fastlocals = f->f_localsplus;
+ fastlocals = __Pyx_PyFrame_GetLocalsplus(f);
for (i = 0; i < na; i++) {
Py_INCREF(*args);
@@ -1510,8 +2512,7 @@
}
-#if 1 || PY_VERSION_HEX < 0x030600B1
-static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) {
+static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) {
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
PyObject *globals = PyFunction_GET_GLOBALS(func);
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
@@ -1531,7 +2532,7 @@
assert(kwargs == NULL || PyDict_Check(kwargs));
nk = kwargs ? PyDict_Size(kwargs) : 0;
- if (Py_EnterRecursiveCall((char*)" while calling a Python object")) {
+ if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) {
return NULL;
}
@@ -1605,12 +2606,12 @@
//#elif PY_MAJOR_VERSION >= 3
#if PY_MAJOR_VERSION >= 3
result = PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL,
- args, nargs,
+ args, (int)nargs,
k, (int)nk,
d, (int)nd, kwdefs, closure);
#else
result = PyEval_EvalCodeEx(co, globals, (PyObject *)NULL,
- args, nargs,
+ args, (int)nargs,
k, (int)nk,
d, (int)nd, closure);
#endif
@@ -1620,41 +2621,20 @@
Py_LeaveRecursiveCall();
return result;
}
-#endif // CPython < 3.6
-#endif // CYTHON_FAST_PYCALL
-
-
-/////////////// PyCFunctionFastCall.proto ///////////////
+#endif /* CYTHON_FAST_PYCALL && !CYTHON_VECTORCALL */
-#if CYTHON_FAST_PYCCALL
-static CYTHON_INLINE PyObject *__Pyx_PyCFunction_FastCall(PyObject *func, PyObject **args, Py_ssize_t nargs);
-#else
-#define __Pyx_PyCFunction_FastCall(func, args, nargs) (assert(0), NULL)
-#endif
-/////////////// PyCFunctionFastCall ///////////////
-
-#if CYTHON_FAST_PYCCALL
-static CYTHON_INLINE PyObject * __Pyx_PyCFunction_FastCall(PyObject *func_obj, PyObject **args, Py_ssize_t nargs) {
- PyCFunctionObject *func = (PyCFunctionObject*)func_obj;
- PyCFunction meth = PyCFunction_GET_FUNCTION(func);
- PyObject *self = PyCFunction_GET_SELF(func);
- PyObject *result;
- int flags;
+/////////////// PyObjectCall2Args.proto ///////////////
- assert(PyCFunction_Check(func));
- assert(METH_FASTCALL == PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST));
- assert(nargs >= 0);
- assert(nargs == 0 || args != NULL);
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2); /*proto*/
- /* _PyCFunction_FastCallDict() must not be called with an exception set,
- because it may clear it (directly or indirectly) and so the
- caller loses its exception */
- assert(!PyErr_Occurred());
+/////////////// PyObjectCall2Args ///////////////
+//@requires: PyObjectFastCall
- return (*((__Pyx_PyCFunctionFast)meth)) (self, args, nargs, NULL);
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) {
+ PyObject *args[3] = {NULL, arg1, arg2};
+ return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
}
-#endif // CYTHON_FAST_PYCCALL
/////////////// PyObjectCallOneArg.proto ///////////////
@@ -1662,91 +2642,97 @@
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg); /*proto*/
/////////////// PyObjectCallOneArg ///////////////
-//@requires: PyObjectCallMethO
-//@requires: PyObjectCall
-//@requires: PyFunctionFastCall
-//@requires: PyCFunctionFastCall
-
-#if CYTHON_COMPILING_IN_CPYTHON
-static PyObject* __Pyx__PyObject_CallOneArg(PyObject *func, PyObject *arg) {
- PyObject *result;
- PyObject *args = PyTuple_New(1);
- if (unlikely(!args)) return NULL;
- Py_INCREF(arg);
- PyTuple_SET_ITEM(args, 0, arg);
- result = __Pyx_PyObject_Call(func, args, NULL);
- Py_DECREF(args);
- return result;
-}
+//@requires: PyObjectFastCall
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
-#if CYTHON_FAST_PYCALL
- if (PyFunction_Check(func)) {
- return __Pyx_PyFunction_FastCall(func, &arg, 1);
- }
-#endif
-#ifdef __Pyx_CyFunction_USED
- if (likely(PyCFunction_Check(func) || PyObject_TypeCheck(func, __pyx_CyFunctionType))) {
-#else
- if (likely(PyCFunction_Check(func))) {
-#endif
- if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
- // fast and simple case that we are optimising for
- return __Pyx_PyObject_CallMethO(func, arg);
-#if CYTHON_FAST_PYCCALL
- } else if (PyCFunction_GET_FLAGS(func) & METH_FASTCALL) {
- return __Pyx_PyCFunction_FastCall(func, &arg, 1);
-#endif
- }
- }
- return __Pyx__PyObject_CallOneArg(func, arg);
+ PyObject *args[2] = {NULL, arg};
+ return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
}
-#else
-static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
- PyObject *result;
- PyObject *args = PyTuple_Pack(1, arg);
- if (unlikely(!args)) return NULL;
- result = __Pyx_PyObject_Call(func, args, NULL);
- Py_DECREF(args);
- return result;
-}
-#endif
/////////////// PyObjectCallNoArg.proto ///////////////
-//@requires: PyObjectCall
-//@substitute: naming
-#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); /*proto*/
-#else
-#define __Pyx_PyObject_CallNoArg(func) __Pyx_PyObject_Call(func, $empty_tuple, NULL)
-#endif
/////////////// PyObjectCallNoArg ///////////////
-//@requires: PyObjectCallMethO
-//@requires: PyObjectCall
-//@requires: PyFunctionFastCall
-//@substitute: naming
+//@requires: PyObjectFastCall
-#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) {
-#if CYTHON_FAST_PYCALL
- if (PyFunction_Check(func)) {
- return __Pyx_PyFunction_FastCall(func, NULL, 0);
+ PyObject *arg = NULL;
+ return __Pyx_PyObject_FastCall(func, (&arg)+1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+
+/////////////// PyVectorcallFastCallDict.proto ///////////////
+
+#if CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw);
+#endif
+
+/////////////// PyVectorcallFastCallDict ///////////////
+
+#if CYTHON_METH_FASTCALL
+// Slow path when kw is non-empty
+static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ // Code based on _PyObject_FastCallDict() and _PyStack_UnpackDict() from CPython
+ PyObject *res = NULL;
+ PyObject *kwnames;
+ PyObject **newargs;
+ PyObject **kwvalues;
+ Py_ssize_t i, pos;
+ size_t j;
+ PyObject *key, *value;
+ unsigned long keys_are_strings;
+ Py_ssize_t nkw = PyDict_GET_SIZE(kw);
+
+ // Copy positional arguments
+ newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0]));
+ if (unlikely(newargs == NULL)) {
+ PyErr_NoMemory();
+ return NULL;
}
-#endif
-#ifdef __Pyx_CyFunction_USED
- if (likely(PyCFunction_Check(func) || PyObject_TypeCheck(func, __pyx_CyFunctionType))) {
-#else
- if (likely(PyCFunction_Check(func))) {
-#endif
- if (likely(PyCFunction_GET_FLAGS(func) & METH_NOARGS)) {
- // fast and simple case that we are optimising for
- return __Pyx_PyObject_CallMethO(func, NULL);
- }
+ for (j = 0; j < nargs; j++) newargs[j] = args[j];
+
+ // Copy keyword arguments
+ kwnames = PyTuple_New(nkw);
+ if (unlikely(kwnames == NULL)) {
+ PyMem_Free(newargs);
+ return NULL;
+ }
+ kwvalues = newargs + nargs;
+ pos = i = 0;
+ keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;
+ while (PyDict_Next(kw, &pos, &key, &value)) {
+ keys_are_strings &= Py_TYPE(key)->tp_flags;
+ Py_INCREF(key);
+ Py_INCREF(value);
+ PyTuple_SET_ITEM(kwnames, i, key);
+ kwvalues[i] = value;
+ i++;
+ }
+ if (unlikely(!keys_are_strings)) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ goto cleanup;
+ }
+
+ // The actual call
+ res = vc(func, newargs, nargs, kwnames);
+
+cleanup:
+ Py_DECREF(kwnames);
+ for (i = 0; i < nkw; i++)
+ Py_DECREF(kwvalues[i]);
+ PyMem_Free(newargs);
+ return res;
+}
+
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ if (likely(kw == NULL) || PyDict_GET_SIZE(kw) == 0) {
+ return vc(func, args, nargs, NULL);
}
- return __Pyx_PyObject_Call(func, $empty_tuple, NULL);
+ return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw);
}
#endif
@@ -1763,10 +2749,9 @@
#endif
/////////////// MatrixMultiply ///////////////
-//@requires: PyObjectGetAttrStr
+//@requires: PyObjectGetAttrStrNoError
//@requires: PyObjectCallOneArg
-//@requires: PyFunctionFastCall
-//@requires: PyCFunctionFastCall
+//@requires: PyObjectCall2Args
#if PY_VERSION_HEX < 0x03050000
static PyObject* __Pyx_PyObject_CallMatrixMethod(PyObject* method, PyObject* arg) {
@@ -1776,34 +2761,9 @@
if (likely(PyMethod_Check(method))) {
PyObject *self = PyMethod_GET_SELF(method);
if (likely(self)) {
- PyObject *args;
PyObject *function = PyMethod_GET_FUNCTION(method);
- #if CYTHON_FAST_PYCALL
- if (PyFunction_Check(function)) {
- PyObject *args[2] = {self, arg};
- result = __Pyx_PyFunction_FastCall(function, args, 2);
- goto done;
- }
- #endif
- #if CYTHON_FAST_PYCCALL
- if (__Pyx_PyFastCFunction_Check(function)) {
- PyObject *args[2] = {self, arg};
- result = __Pyx_PyCFunction_FastCall(function, args, 2);
- goto done;
- }
- #endif
- args = PyTuple_New(2);
- if (unlikely(!args)) goto done;
- Py_INCREF(self);
- PyTuple_SET_ITEM(args, 0, self);
- Py_INCREF(arg);
- PyTuple_SET_ITEM(args, 1, arg);
- Py_INCREF(function);
- Py_DECREF(method); method = NULL;
- result = __Pyx_PyObject_Call(function, args, NULL);
- Py_DECREF(args);
- Py_DECREF(function);
- return result;
+ result = __Pyx_PyObject_Call2Args(function, self, arg);
+ goto done;
}
}
#endif
@@ -1813,21 +2773,21 @@
return result;
}
-#define __Pyx_TryMatrixMethod(x, y, py_method_name) { \
- PyObject *func = __Pyx_PyObject_GetAttrStr(x, py_method_name); \
+#define __Pyx_TryMatrixMethod(x, y, py_method_name) { \
+ PyObject *func = __Pyx_PyObject_GetAttrStrNoError(x, py_method_name); \
if (func) { \
PyObject *result = __Pyx_PyObject_CallMatrixMethod(func, y); \
if (result != Py_NotImplemented) \
return result; \
Py_DECREF(result); \
- } else { \
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) \
- return NULL; \
- PyErr_Clear(); \
+ } else if (unlikely(PyErr_Occurred())) { \
+ return NULL; \
} \
}
static PyObject* __Pyx__PyNumber_MatrixMultiply(PyObject* x, PyObject* y, const char* op_name) {
+ __Pyx_TypeName x_type_name;
+ __Pyx_TypeName y_type_name;
int right_is_subtype = PyObject_IsSubclass((PyObject*)Py_TYPE(y), (PyObject*)Py_TYPE(x));
if (unlikely(right_is_subtype == -1))
return NULL;
@@ -1840,11 +2800,13 @@
if (!right_is_subtype) {
__Pyx_TryMatrixMethod(y, x, PYIDENT("__rmatmul__"))
}
+ x_type_name = __Pyx_PyType_GetName(Py_TYPE(x));
+ y_type_name = __Pyx_PyType_GetName(Py_TYPE(y));
PyErr_Format(PyExc_TypeError,
- "unsupported operand type(s) for %.2s: '%.100s' and '%.100s'",
- op_name,
- Py_TYPE(x)->tp_name,
- Py_TYPE(y)->tp_name);
+ "unsupported operand type(s) for %.2s: '" __Pyx_FMT_TYPENAME "' and '"
+ __Pyx_FMT_TYPENAME "'", op_name, x_type_name, y_type_name);
+ __Pyx_DECREF_TypeName(x_type_name);
+ __Pyx_DECREF_TypeName(y_type_name);
return NULL;
}
@@ -1855,3 +2817,298 @@
#undef __Pyx_TryMatrixMethod
#endif
+
+
+/////////////// PyDictVersioning.proto ///////////////
+
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1)
+#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var) \
+ (version_var) = __PYX_GET_DICT_VERSION(dict); \
+ (cache_var) = (value);
+
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) { \
+ static PY_UINT64_T __pyx_dict_version = 0; \
+ static PyObject *__pyx_dict_cached_value = NULL; \
+ if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) { \
+ (VAR) = __pyx_dict_cached_value; \
+ } else { \
+ (VAR) = __pyx_dict_cached_value = (LOOKUP); \
+ __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT); \
+ } \
+}
+
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj); /*proto*/
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj); /*proto*/
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version); /*proto*/
+
+#else
+#define __PYX_GET_DICT_VERSION(dict) (0)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP);
+#endif
+
+/////////////// PyDictVersioning ///////////////
+
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0;
+}
+
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) {
+ PyObject **dictptr = NULL;
+ Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset;
+ if (offset) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj);
+#else
+ dictptr = _PyObject_GetDictPtr(obj);
+#endif
+ }
+ return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0;
+}
+
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict)))
+ return 0;
+ return obj_dict_version == __Pyx_get_object_dict_version(obj);
+}
+#endif
+
+
+/////////////// PyMethodNew.proto ///////////////
+
+#if PY_MAJOR_VERSION >= 3
+// This should be an actual function (not a macro), such that we can put it
+// directly in a tp_descr_get slot.
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) {
+ CYTHON_UNUSED_VAR(typ);
+ if (!self)
+ return __Pyx_NewRef(func);
+ return PyMethod_New(func, self);
+}
+#else
+ #define __Pyx_PyMethod_New PyMethod_New
+#endif
+
+/////////////// UnicodeConcatInPlace.proto ////////////////
+
+# if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3
+// __Pyx_PyUnicode_ConcatInPlace may modify the first argument 'left'
+// However, unlike `PyUnicode_Append` it will never NULL it.
+// It behaves like a regular function - returns a new reference and NULL on error
+ #if CYTHON_REFNANNY
+ #define __Pyx_PyUnicode_ConcatInPlace(left, right) __Pyx_PyUnicode_ConcatInPlaceImpl(&left, right, __pyx_refnanny)
+ #else
+ #define __Pyx_PyUnicode_ConcatInPlace(left, right) __Pyx_PyUnicode_ConcatInPlaceImpl(&left, right)
+ #endif
+ // __Pyx_PyUnicode_ConcatInPlace is slightly odd because it has the potential to modify the input
+ // argument (but only in cases where no user should notice). Therefore, it needs to keep Cython's
+ // refnanny informed.
+ static CYTHON_INLINE PyObject *__Pyx_PyUnicode_ConcatInPlaceImpl(PyObject **p_left, PyObject *right
+ #if CYTHON_REFNANNY
+ , void* __pyx_refnanny
+ #endif
+ ); /* proto */
+#else
+#define __Pyx_PyUnicode_ConcatInPlace __Pyx_PyUnicode_Concat
+#endif
+#define __Pyx_PyUnicode_ConcatInPlaceSafe(left, right) ((unlikely((left) == Py_None) || unlikely((right) == Py_None)) ? \
+ PyNumber_InPlaceAdd(left, right) : __Pyx_PyUnicode_ConcatInPlace(left, right))
+
+/////////////// UnicodeConcatInPlace ////////////////
+//@substitute: naming
+
+# if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3
+// copied directly from unicode_object.c "unicode_modifiable
+// removing _PyUnicode_HASH since it's a macro we don't have
+// - this is OK because trying PyUnicode_Resize on a non-modifyable
+// object will still work, it just won't happen in place
+static int
+__Pyx_unicode_modifiable(PyObject *unicode)
+{
+ if (Py_REFCNT(unicode) != 1)
+ return 0;
+ if (!PyUnicode_CheckExact(unicode))
+ return 0;
+ if (PyUnicode_CHECK_INTERNED(unicode))
+ return 0;
+ return 1;
+}
+
+static CYTHON_INLINE PyObject *__Pyx_PyUnicode_ConcatInPlaceImpl(PyObject **p_left, PyObject *right
+ #if CYTHON_REFNANNY
+ , void* __pyx_refnanny
+ #endif
+ ) {
+ // heavily based on PyUnicode_Append
+ PyObject *left = *p_left;
+ Py_ssize_t left_len, right_len, new_len;
+
+ if (unlikely(__Pyx_PyUnicode_READY(left) == -1))
+ return NULL;
+ if (unlikely(__Pyx_PyUnicode_READY(right) == -1))
+ return NULL;
+
+ // Shortcuts
+ left_len = PyUnicode_GET_LENGTH(left);
+ if (left_len == 0) {
+ Py_INCREF(right);
+ return right;
+ }
+ right_len = PyUnicode_GET_LENGTH(right);
+ if (right_len == 0) {
+ Py_INCREF(left);
+ return left;
+ }
+ if (unlikely(left_len > PY_SSIZE_T_MAX - right_len)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "strings are too large to concat");
+ return NULL;
+ }
+ new_len = left_len + right_len;
+
+ if (__Pyx_unicode_modifiable(left)
+ && PyUnicode_CheckExact(right)
+ && PyUnicode_KIND(right) <= PyUnicode_KIND(left)
+ // Don't resize for ascii += latin1. Convert ascii to latin1 requires
+ // to change the structure size, but characters are stored just after
+ // the structure, and so it requires to move all characters which is
+ // not so different than duplicating the string.
+ && !(PyUnicode_IS_ASCII(left) && !PyUnicode_IS_ASCII(right))) {
+
+ __Pyx_GIVEREF(*p_left);
+ if (unlikely(PyUnicode_Resize(p_left, new_len) != 0)) {
+ // on failure PyUnicode_Resize does not deallocate the the input
+ // so left will remain unchanged - simply undo the giveref
+ __Pyx_GOTREF(*p_left);
+ return NULL;
+ }
+ __Pyx_INCREF(*p_left);
+
+ // copy 'right' into the newly allocated area of 'left'
+ _PyUnicode_FastCopyCharacters(*p_left, left_len, right, 0, right_len);
+ return *p_left;
+ } else {
+ return __Pyx_PyUnicode_Concat(left, right);
+ }
+ }
+#endif
+
+////////////// StrConcatInPlace.proto ///////////////////////
+//@requires: UnicodeConcatInPlace
+
+#if PY_MAJOR_VERSION >= 3
+ // allow access to the more efficient versions where we know str_type is unicode
+ #define __Pyx_PyStr_Concat __Pyx_PyUnicode_Concat
+ #define __Pyx_PyStr_ConcatInPlace __Pyx_PyUnicode_ConcatInPlace
+#else
+ #define __Pyx_PyStr_Concat PyNumber_Add
+ #define __Pyx_PyStr_ConcatInPlace PyNumber_InPlaceAdd
+#endif
+#define __Pyx_PyStr_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ? \
+ PyNumber_Add(a, b) : __Pyx_PyStr_Concat(a, b))
+#define __Pyx_PyStr_ConcatInPlaceSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ? \
+ PyNumber_InPlaceAdd(a, b) : __Pyx_PyStr_ConcatInPlace(a, b))
+
+/////////////// FormatTypeName.proto ///////////////
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+typedef PyObject *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%U"
+static __Pyx_TypeName __Pyx_PyType_GetName(PyTypeObject* tp); /*proto*/
+#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj)
+#else
+typedef const char *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%.200s"
+#define __Pyx_PyType_GetName(tp) ((tp)->tp_name)
+#define __Pyx_DECREF_TypeName(obj)
+#endif
+
+/////////////// FormatTypeName ///////////////
+
+#if CYTHON_COMPILING_IN_LIMITED_API
+static __Pyx_TypeName
+__Pyx_PyType_GetName(PyTypeObject* tp)
+{
+ PyObject *name = __Pyx_PyObject_GetAttrStr((PyObject *)tp,
+ PYIDENT("__name__"));
+ if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) {
+ PyErr_Clear();
+ Py_XSETREF(name, __Pyx_NewRef(PYIDENT("?")));
+ }
+ return name;
+}
+#endif
+
+
+/////////////// RaiseUnexpectedTypeError.proto ///////////////
+
+static int __Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj); /*proto*/
+
+/////////////// RaiseUnexpectedTypeError ///////////////
+
+static int
+__Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj)
+{
+ __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError, "Expected %s, got " __Pyx_FMT_TYPENAME,
+ expected, obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return 0;
+}
+
+
+/////////////// RaiseUnboundLocalError.proto ///////////////
+static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);/*proto*/
+
+/////////////// RaiseUnboundLocalError ///////////////
+static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
+ PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
+}
+
+
+/////////////// RaiseClosureNameError.proto ///////////////
+static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);/*proto*/
+
+/////////////// RaiseClosureNameError ///////////////
+static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
+ PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
+}
+
+
+/////////////// RaiseUnboundMemoryviewSliceNogil.proto ///////////////
+static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);/*proto*/
+
+/////////////// RaiseUnboundMemoryviewSliceNogil ///////////////
+//@requires: RaiseUnboundLocalError
+
+// Don't inline the function, it should really never be called in production
+static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
+ #ifdef WITH_THREAD
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ #endif
+ __Pyx_RaiseUnboundLocalError(varname);
+ #ifdef WITH_THREAD
+ PyGILState_Release(gilstate);
+ #endif
+}
+
+//////////////// RaiseCppGlobalNameError.proto ///////////////////////
+static CYTHON_INLINE void __Pyx_RaiseCppGlobalNameError(const char *varname); /*proto*/
+
+/////////////// RaiseCppGlobalNameError //////////////////////////////
+static CYTHON_INLINE void __Pyx_RaiseCppGlobalNameError(const char *varname) {
+ PyErr_Format(PyExc_NameError, "C++ global '%s' is not initialized", varname);
+}
+
+//////////////// RaiseCppAttributeError.proto ///////////////////////
+static CYTHON_INLINE void __Pyx_RaiseCppAttributeError(const char *varname); /*proto*/
+
+/////////////// RaiseCppAttributeError //////////////////////////////
+static CYTHON_INLINE void __Pyx_RaiseCppAttributeError(const char *varname) {
+ PyErr_Format(PyExc_AttributeError, "C++ attribute '%s' is not initialized", varname);
+}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Optimize.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Optimize.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Optimize.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Optimize.c 2022-03-24 10:16:46.000000000 +0000
@@ -35,7 +35,7 @@
if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) {
Py_INCREF(x);
PyList_SET_ITEM(list, len, x);
- Py_SIZE(list) = len+1;
+ __Pyx_SET_SIZE(list, len + 1);
return 0;
}
return PyList_Append(list, x);
@@ -53,7 +53,7 @@
if (likely(L->allocated > len)) {
Py_INCREF(x);
PyList_SET_ITEM(list, len, x);
- Py_SIZE(list) = len+1;
+ __Pyx_SET_SIZE(list, len + 1);
return 0;
}
return PyList_Append(list, x);
@@ -94,7 +94,7 @@
//@requires: ObjectHandling.c::PyObjectCallMethod0
static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) {
- if (Py_TYPE(L) == &PySet_Type) {
+ if (__Pyx_IS_TYPE(L, &PySet_Type)) {
return PySet_Pop(L);
}
return __Pyx_PyObject_CallMethod0(L, PYIDENT("pop"));
@@ -104,7 +104,7 @@
static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) {
/* Check that both the size is positive and no reallocation shrinking needs to be done. */
if (likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) {
- Py_SIZE(L) -= 1;
+ __Pyx_SET_SIZE(L, Py_SIZE(L) - 1);
return PyList_GET_ITEM(L, PyList_GET_SIZE(L));
}
return CALL_UNBOUND_METHOD(PyList_Type, "pop", L);
@@ -123,13 +123,13 @@
#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
(likely(PyList_CheckExact(L) && __Pyx_fits_Py_ssize_t(ix, type, is_signed))) ? \
__Pyx__PyList_PopIndex(L, py_ix, ix) : ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix)))
#define __Pyx_PyList_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
__Pyx_fits_Py_ssize_t(ix, type, is_signed) ? \
__Pyx__PyList_PopIndex(L, py_ix, ix) : ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix)))
#else
@@ -138,7 +138,7 @@
__Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func)
#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \
- (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
+ (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \
__Pyx__PyObject_PopIndex(L, py_ix))
#endif
@@ -165,9 +165,9 @@
if (cix < 0) {
cix += size;
}
- if (likely(0 <= cix && cix < size)) {
+ if (likely(__Pyx_is_valid_index(cix, size))) {
PyObject* v = PyList_GET_ITEM(L, cix);
- Py_SIZE(L) -= 1;
+ __Pyx_SET_SIZE(L, Py_SIZE(L) - 1);
size -= 1;
memmove(&PyList_GET_ITEM(L, cix), &PyList_GET_ITEM(L, cix+1), (size_t)(size-cix)*sizeof(PyObject*));
return v;
@@ -190,7 +190,7 @@
static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObject* default_value) {
PyObject* value;
-#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
+#if PY_MAJOR_VERSION >= 3 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000)
value = PyDict_GetItemWithError(d, key);
if (unlikely(!value)) {
if (unlikely(PyErr_Occurred()))
@@ -198,6 +198,8 @@
value = default_value;
}
Py_INCREF(value);
+ // avoid C compiler warning about unused utility functions
+ if ((1));
#else
if (PyString_CheckExact(key) || PyUnicode_CheckExact(key) || PyInt_CheckExact(key)) {
/* these presumably have safe hash functions */
@@ -206,13 +208,14 @@
value = default_value;
}
Py_INCREF(value);
- } else {
- if (default_value == Py_None)
- default_value = NULL;
- value = PyObject_CallMethodObjArgs(
- d, PYIDENT("get"), key, default_value, NULL);
}
#endif
+ else {
+ if (default_value == Py_None)
+ value = CALL_UNBOUND_METHOD(PyDict_Type, "get", d, key);
+ else
+ value = CALL_UNBOUND_METHOD(PyDict_Type, "get", d, key, default_value);
+ }
return value;
}
@@ -222,21 +225,21 @@
static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value, int is_safe_type); /*proto*/
/////////////// dict_setdefault ///////////////
-//@requires: ObjectHandling.c::PyObjectCallMethod2
static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value,
- CYTHON_UNUSED int is_safe_type) {
+ int is_safe_type) {
PyObject* value;
+ CYTHON_MAYBE_UNUSED_VAR(is_safe_type);
#if PY_VERSION_HEX >= 0x030400A0
// we keep the method call at the end to avoid "unused" C compiler warnings
- if (1) {
+ if ((1)) {
value = PyDict_SetDefault(d, key, default_value);
if (unlikely(!value)) return NULL;
Py_INCREF(value);
#else
if (is_safe_type == 1 || (is_safe_type == -1 &&
/* the following builtins presumably have repeatably safe and fast hash functions */
-#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
+#if PY_MAJOR_VERSION >= 3 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000)
(PyUnicode_CheckExact(key) || PyString_CheckExact(key) || PyLong_CheckExact(key)))) {
value = PyDict_GetItemWithError(d, key);
if (unlikely(!value)) {
@@ -259,7 +262,7 @@
#endif
#endif
} else {
- value = __Pyx_PyObject_CallMethod2(d, PYIDENT("setdefault"), key, default_value);
+ value = CALL_UNBOUND_METHOD(PyDict_Type, "setdefault", d, key, default_value);
}
return value;
}
@@ -269,6 +272,28 @@
#define __Pyx_PyDict_Clear(d) (PyDict_Clear(d), 0)
+
+/////////////// py_dict_pop.proto ///////////////
+
+static CYTHON_INLINE PyObject *__Pyx_PyDict_Pop(PyObject *d, PyObject *key, PyObject *default_value); /*proto*/
+
+/////////////// py_dict_pop ///////////////
+
+static CYTHON_INLINE PyObject *__Pyx_PyDict_Pop(PyObject *d, PyObject *key, PyObject *default_value) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX > 0x030600B3
+ if ((1)) {
+ return _PyDict_Pop(d, key, default_value);
+ } else
+ // avoid "function unused" warnings
+#endif
+ if (default_value) {
+ return CALL_UNBOUND_METHOD(PyDict_Type, "pop", d, key, default_value);
+ } else {
+ return CALL_UNBOUND_METHOD(PyDict_Type, "pop", d, key);
+ }
+}
+
+
/////////////// dict_iter.proto ///////////////
static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* dict, int is_dict, PyObject* method_name,
@@ -285,13 +310,32 @@
Py_ssize_t* p_orig_length, int* p_source_is_dict) {
is_dict = is_dict || likely(PyDict_CheckExact(iterable));
*p_source_is_dict = is_dict;
-#if !CYTHON_COMPILING_IN_PYPY
if (is_dict) {
+#if !CYTHON_COMPILING_IN_PYPY
*p_orig_length = PyDict_Size(iterable);
Py_INCREF(iterable);
return iterable;
- }
+#elif PY_MAJOR_VERSION >= 3
+ // On PyPy3, we need to translate manually a few method names.
+ // This logic is not needed on CPython thanks to the fast case above.
+ static PyObject *py_items = NULL, *py_keys = NULL, *py_values = NULL;
+ PyObject **pp = NULL;
+ if (method_name) {
+ const char *name = PyUnicode_AsUTF8(method_name);
+ if (strcmp(name, "iteritems") == 0) pp = &py_items;
+ else if (strcmp(name, "iterkeys") == 0) pp = &py_keys;
+ else if (strcmp(name, "itervalues") == 0) pp = &py_values;
+ if (pp) {
+ if (!*pp) {
+ *pp = PyUnicode_FromString(name + 4);
+ if (!*pp)
+ return NULL;
+ }
+ method_name = *pp;
+ }
+ }
#endif
+ }
*p_orig_length = 0;
if (method_name) {
PyObject* iter;
@@ -378,6 +422,143 @@
}
+/////////////// set_iter.proto ///////////////
+
+static CYTHON_INLINE PyObject* __Pyx_set_iterator(PyObject* iterable, int is_set,
+ Py_ssize_t* p_orig_length, int* p_source_is_set); /*proto*/
+static CYTHON_INLINE int __Pyx_set_iter_next(
+ PyObject* iter_obj, Py_ssize_t orig_length,
+ Py_ssize_t* ppos, PyObject **value,
+ int source_is_set); /*proto*/
+
+/////////////// set_iter ///////////////
+//@requires: ObjectHandling.c::IterFinish
+
+static CYTHON_INLINE PyObject* __Pyx_set_iterator(PyObject* iterable, int is_set,
+ Py_ssize_t* p_orig_length, int* p_source_is_set) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ is_set = is_set || likely(PySet_CheckExact(iterable) || PyFrozenSet_CheckExact(iterable));
+ *p_source_is_set = is_set;
+ if (likely(is_set)) {
+ *p_orig_length = PySet_Size(iterable);
+ Py_INCREF(iterable);
+ return iterable;
+ }
+#else
+ (void)is_set;
+ *p_source_is_set = 0;
+#endif
+ *p_orig_length = 0;
+ return PyObject_GetIter(iterable);
+}
+
+static CYTHON_INLINE int __Pyx_set_iter_next(
+ PyObject* iter_obj, Py_ssize_t orig_length,
+ Py_ssize_t* ppos, PyObject **value,
+ int source_is_set) {
+ if (!CYTHON_COMPILING_IN_CPYTHON || unlikely(!source_is_set)) {
+ *value = PyIter_Next(iter_obj);
+ if (unlikely(!*value)) {
+ return __Pyx_IterFinish();
+ }
+ (void)orig_length;
+ (void)ppos;
+ return 1;
+ }
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (unlikely(PySet_GET_SIZE(iter_obj) != orig_length)) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "set changed size during iteration");
+ return -1;
+ }
+ {
+ Py_hash_t hash;
+ int ret = _PySet_NextEntry(iter_obj, ppos, value, &hash);
+ // CPython does not raise errors here, only if !isinstance(iter_obj, set/frozenset)
+ assert (ret != -1);
+ if (likely(ret)) {
+ Py_INCREF(*value);
+ return 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/////////////// py_set_discard_unhashable ///////////////
+//@requires: Builtins.c::pyfrozenset_new
+
+static int __Pyx_PySet_DiscardUnhashable(PyObject *set, PyObject *key) {
+ PyObject *tmpkey;
+ int rv;
+
+ if (likely(!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)))
+ return -1;
+ PyErr_Clear();
+ tmpkey = __Pyx_PyFrozenSet_New(key);
+ if (tmpkey == NULL)
+ return -1;
+ rv = PySet_Discard(set, tmpkey);
+ Py_DECREF(tmpkey);
+ return rv;
+}
+
+
+/////////////// py_set_discard.proto ///////////////
+
+static CYTHON_INLINE int __Pyx_PySet_Discard(PyObject *set, PyObject *key); /*proto*/
+
+/////////////// py_set_discard ///////////////
+//@requires: py_set_discard_unhashable
+
+static CYTHON_INLINE int __Pyx_PySet_Discard(PyObject *set, PyObject *key) {
+ int found = PySet_Discard(set, key);
+ // Convert *key* to frozenset if necessary
+ if (unlikely(found < 0)) {
+ found = __Pyx_PySet_DiscardUnhashable(set, key);
+ }
+ // note: returns -1 on error, 0 (not found) or 1 (found) otherwise => error check for -1 or < 0 works
+ return found;
+}
+
+
+/////////////// py_set_remove.proto ///////////////
+
+static CYTHON_INLINE int __Pyx_PySet_Remove(PyObject *set, PyObject *key); /*proto*/
+
+/////////////// py_set_remove ///////////////
+//@requires: py_set_discard_unhashable
+
+static int __Pyx_PySet_RemoveNotFound(PyObject *set, PyObject *key, int found) {
+ // Convert *key* to frozenset if necessary
+ if (unlikely(found < 0)) {
+ found = __Pyx_PySet_DiscardUnhashable(set, key);
+ }
+ if (likely(found == 0)) {
+ // Not found
+ PyObject *tup;
+ tup = PyTuple_Pack(1, key);
+ if (!tup)
+ return -1;
+ PyErr_SetObject(PyExc_KeyError, tup);
+ Py_DECREF(tup);
+ return -1;
+ }
+ // note: returns -1 on error, 0 (not found) or 1 (found) otherwise => error check for -1 or < 0 works
+ return found;
+}
+
+static CYTHON_INLINE int __Pyx_PySet_Remove(PyObject *set, PyObject *key) {
+ int found = PySet_Discard(set, key);
+ if (unlikely(found != 1)) {
+ // note: returns -1 on error, 0 (not found) or 1 (found) otherwise => error check for -1 or < 0 works
+ return __Pyx_PySet_RemoveNotFound(set, key, found);
+ }
+ return 0;
+}
+
+
/////////////// unicode_iter.proto ///////////////
static CYTHON_INLINE int __Pyx_init_unicode_iteration(
@@ -411,51 +592,366 @@
PyFloat_AsDouble(obj) : __Pyx__PyObject_AsDouble(obj))
#else
#define __Pyx_PyObject_AsDouble(obj) \
-((likely(PyFloat_CheckExact(obj))) ? \
- PyFloat_AS_DOUBLE(obj) : __Pyx__PyObject_AsDouble(obj))
+((likely(PyFloat_CheckExact(obj))) ? PyFloat_AS_DOUBLE(obj) : \
+ likely(PyLong_CheckExact(obj)) ? \
+ PyLong_AsDouble(obj) : __Pyx__PyObject_AsDouble(obj))
#endif
/////////////// pyobject_as_double ///////////////
+//@requires: pybytes_as_double
+//@requires: pyunicode_as_double
+//@requires: ObjectHandling.c::PyObjectCallOneArg
static double __Pyx__PyObject_AsDouble(PyObject* obj) {
- PyObject* float_value;
+ if (PyUnicode_CheckExact(obj)) {
+ return __Pyx_PyUnicode_AsDouble(obj);
+ } else if (PyBytes_CheckExact(obj)) {
+ return __Pyx_PyBytes_AsDouble(obj);
+ } else if (PyByteArray_CheckExact(obj)) {
+ return __Pyx_PyByteArray_AsDouble(obj);
+ } else {
+ PyObject* float_value;
#if !CYTHON_USE_TYPE_SLOTS
- float_value = PyNumber_Float(obj); if (0) goto bad;
+ float_value = PyNumber_Float(obj); if ((0)) goto bad;
+ // avoid "unused" warnings
+ (void)__Pyx_PyObject_CallOneArg;
#else
- PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number;
- if (likely(nb) && likely(nb->nb_float)) {
- float_value = nb->nb_float(obj);
- if (likely(float_value) && unlikely(!PyFloat_Check(float_value))) {
- PyErr_Format(PyExc_TypeError,
- "__float__ returned non-float (type %.200s)",
- Py_TYPE(float_value)->tp_name);
- Py_DECREF(float_value);
- goto bad;
+ PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number;
+ if (likely(nb) && likely(nb->nb_float)) {
+ float_value = nb->nb_float(obj);
+ if (likely(float_value) && unlikely(!PyFloat_Check(float_value))) {
+ __Pyx_TypeName float_value_type_name = __Pyx_PyType_GetName(Py_TYPE(float_value));
+ PyErr_Format(PyExc_TypeError,
+ "__float__ returned non-float (type " __Pyx_FMT_TYPENAME ")",
+ float_value_type_name);
+ __Pyx_DECREF_TypeName(float_value_type_name);
+ Py_DECREF(float_value);
+ goto bad;
+ }
+ } else {
+ float_value = __Pyx_PyObject_CallOneArg((PyObject*)&PyFloat_Type, obj);
}
- } else if (PyUnicode_CheckExact(obj) || PyBytes_CheckExact(obj)) {
-#if PY_MAJOR_VERSION >= 3
- float_value = PyFloat_FromString(obj);
-#else
- float_value = PyFloat_FromString(obj, 0);
#endif
+ if (likely(float_value)) {
+ double value = PyFloat_AS_DOUBLE(float_value);
+ Py_DECREF(float_value);
+ return value;
+ }
+ }
+bad:
+ return (double)-1;
+}
+
+
+/////////////// pystring_as_double.proto ///////////////
+//@requires: pyunicode_as_double
+//@requires: pybytes_as_double
+
+static CYTHON_INLINE double __Pyx_PyString_AsDouble(PyObject *obj) {
+ #if PY_MAJOR_VERSION >= 3
+ (void)__Pyx_PyBytes_AsDouble;
+ return __Pyx_PyUnicode_AsDouble(obj);
+ #else
+ (void)__Pyx_PyUnicode_AsDouble;
+ return __Pyx_PyBytes_AsDouble(obj);
+ #endif
+}
+
+
+/////////////// pyunicode_as_double.proto ///////////////
+
+static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj);/*proto*/
+
+/////////////// pyunicode_as_double.proto ///////////////
+//@requires: pybytes_as_double
+
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
+static const char* __Pyx__PyUnicode_AsDouble_Copy(const void* data, const int kind, char* buffer, Py_ssize_t start, Py_ssize_t end) {
+ int last_was_punctuation;
+ Py_ssize_t i;
+ // number must not start with punctuation
+ last_was_punctuation = 1;
+ for (i=start; i <= end; i++) {
+ Py_UCS4 chr = PyUnicode_READ(kind, data, i);
+ int is_punctuation = (chr == '_') | (chr == '.');
+ *buffer = (char)chr;
+ // reject sequences of '_' and '.'
+ buffer += (chr != '_');
+ if (unlikely(chr > 127)) goto parse_failure;
+ if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure;
+ last_was_punctuation = is_punctuation;
+ }
+ if (unlikely(last_was_punctuation)) goto parse_failure;
+ *buffer = '\0';
+ return buffer;
+
+parse_failure:
+ return NULL;
+}
+
+static double __Pyx__PyUnicode_AsDouble_inf_nan(const void* data, int kind, Py_ssize_t start, Py_ssize_t length) {
+ int matches = 1;
+ Py_UCS4 chr;
+ Py_UCS4 sign = PyUnicode_READ(kind, data, start);
+ int is_signed = (sign == '-') | (sign == '+');
+ start += is_signed;
+ length -= is_signed;
+
+ switch (PyUnicode_READ(kind, data, start)) {
+ #ifdef Py_NAN
+ case 'n':
+ case 'N':
+ if (unlikely(length != 3)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+1);
+ matches &= (chr == 'a') | (chr == 'A');
+ chr = PyUnicode_READ(kind, data, start+2);
+ matches &= (chr == 'n') | (chr == 'N');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_NAN : Py_NAN;
+ #endif
+ case 'i':
+ case 'I':
+ if (unlikely(length < 3)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+1);
+ matches &= (chr == 'n') | (chr == 'N');
+ chr = PyUnicode_READ(kind, data, start+2);
+ matches &= (chr == 'f') | (chr == 'F');
+ if (likely(length == 3 && matches))
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ if (unlikely(length != 8)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+3);
+ matches &= (chr == 'i') | (chr == 'I');
+ chr = PyUnicode_READ(kind, data, start+4);
+ matches &= (chr == 'n') | (chr == 'N');
+ chr = PyUnicode_READ(kind, data, start+5);
+ matches &= (chr == 'i') | (chr == 'I');
+ chr = PyUnicode_READ(kind, data, start+6);
+ matches &= (chr == 't') | (chr == 'T');
+ chr = PyUnicode_READ(kind, data, start+7);
+ matches &= (chr == 'y') | (chr == 'Y');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ break;
+ default:
+ goto parse_failure;
+ }
+ return 0.0;
+parse_failure:
+ return -1.0;
+}
+
+static double __Pyx_PyUnicode_AsDouble_WithSpaces(PyObject *obj) {
+ double value;
+ const char *last;
+ char *end;
+ Py_ssize_t start, length = PyUnicode_GET_LENGTH(obj);
+ const int kind = PyUnicode_KIND(obj);
+ const void* data = PyUnicode_DATA(obj);
+
+ // strip spaces at start and end
+ start = 0;
+ while (Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, start)))
+ start++;
+ while (start < length - 1 && Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, length - 1)))
+ length--;
+ length -= start;
+ if (unlikely(length <= 0)) goto fallback;
+
+ // parse NaN / inf
+ value = __Pyx__PyUnicode_AsDouble_inf_nan(data, kind, start, length);
+ if (unlikely(value == -1.0)) goto fallback;
+ if (value != 0.0) return value;
+
+ if (length < 40) {
+ char number[40];
+ last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+ if (unlikely(!last)) goto fallback;
+ value = PyOS_string_to_double(number, &end, NULL);
} else {
- PyObject* args = PyTuple_New(1);
- if (unlikely(!args)) goto bad;
- PyTuple_SET_ITEM(args, 0, obj);
- float_value = PyObject_Call((PyObject*)&PyFloat_Type, args, 0);
- PyTuple_SET_ITEM(args, 0, 0);
- Py_DECREF(args);
+ char *number = (char*) PyMem_Malloc((length + 1) * sizeof(char));
+ if (unlikely(!number)) goto fallback;
+ last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+ if (unlikely(!last)) {
+ PyMem_Free(number);
+ goto fallback;
+ }
+ value = PyOS_string_to_double(number, &end, NULL);
+ PyMem_Free(number);
+ }
+ if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+ return value;
+ }
+fallback:
+ return __Pyx_SlowPyString_AsDouble(obj);
+}
+#endif
+
+static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) {
+ // Currently not optimised for Py2.7.
+#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY
+ if (unlikely(__Pyx_PyUnicode_READY(obj) == -1))
+ return (double)-1;
+ if (likely(PyUnicode_IS_ASCII(obj))) {
+ const char *s;
+ Py_ssize_t length;
+ s = PyUnicode_AsUTF8AndSize(obj, &length);
+ return __Pyx__PyBytes_AsDouble(obj, s, length);
}
+ return __Pyx_PyUnicode_AsDouble_WithSpaces(obj);
+#else
+ return __Pyx_SlowPyString_AsDouble(obj);
+#endif
+}
+
+
+/////////////// pybytes_as_double.proto ///////////////
+
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj);/*proto*/
+static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length);/*proto*/
+
+static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) {
+ return __Pyx__PyBytes_AsDouble(obj, PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj));
+}
+static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) {
+ return __Pyx__PyBytes_AsDouble(obj, PyByteArray_AS_STRING(obj), PyByteArray_GET_SIZE(obj));
+}
+
+
+/////////////// pybytes_as_double ///////////////
+
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj) {
+ PyObject *float_value;
+#if PY_MAJOR_VERSION >= 3
+ float_value = PyFloat_FromString(obj);
+#else
+ float_value = PyFloat_FromString(obj, 0);
#endif
if (likely(float_value)) {
double value = PyFloat_AS_DOUBLE(float_value);
Py_DECREF(float_value);
return value;
}
-bad:
return (double)-1;
}
+static const char* __Pyx__PyBytes_AsDouble_Copy(const char* start, char* buffer, Py_ssize_t length) {
+ // number must not start with punctuation
+ int last_was_punctuation = 1;
+ Py_ssize_t i;
+ for (i=0; i < length; i++) {
+ char chr = start[i];
+ int is_punctuation = (chr == '_') | (chr == '.') | (chr == 'e') | (chr == 'E');
+ *buffer = chr;
+ buffer += (chr != '_');
+ // reject sequences of '_' and '.'
+ if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure;
+ last_was_punctuation = is_punctuation;
+ }
+ if (unlikely(last_was_punctuation)) goto parse_failure;
+ *buffer = '\0';
+ return buffer;
+
+parse_failure:
+ return NULL;
+}
+
+static double __Pyx__PyBytes_AsDouble_inf_nan(const char* start, Py_ssize_t length) {
+ int matches = 1;
+ char sign = start[0];
+ int is_signed = (sign == '+') | (sign == '-');
+ start += is_signed;
+ length -= is_signed;
+
+ switch (start[0]) {
+ #ifdef Py_NAN
+ case 'n':
+ case 'N':
+ if (unlikely(length != 3)) goto parse_failure;
+ matches &= (start[1] == 'a' || start[1] == 'A');
+ matches &= (start[2] == 'n' || start[2] == 'N');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_NAN : Py_NAN;
+ #endif
+ case 'i':
+ case 'I':
+ if (unlikely(length < 3)) goto parse_failure;
+ matches &= (start[1] == 'n' || start[1] == 'N');
+ matches &= (start[2] == 'f' || start[2] == 'F');
+ if (likely(length == 3 && matches))
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ if (unlikely(length != 8)) goto parse_failure;
+ matches &= (start[3] == 'i' || start[3] == 'I');
+ matches &= (start[4] == 'n' || start[4] == 'N');
+ matches &= (start[5] == 'i' || start[5] == 'I');
+ matches &= (start[6] == 't' || start[6] == 'T');
+ matches &= (start[7] == 'y' || start[7] == 'Y');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ break;
+ default:
+ goto parse_failure;
+ }
+ return 0.0;
+parse_failure:
+ return -1.0;
+}
+
+static CYTHON_INLINE int __Pyx__PyBytes_AsDouble_IsSpace(char ch) {
+ // see Py_ISSPACE() in CPython
+ // https://github.com/python/cpython/blob/master/Python/pyctype.c
+ return (ch == 0x20) | !((ch < 0x9) | (ch > 0xd));
+}
+
+static CYTHON_UNUSED double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) {
+ double value;
+ Py_ssize_t i, digits;
+ const char *last = start + length;
+ char *end;
+
+ // strip spaces at start and end
+ while (__Pyx__PyBytes_AsDouble_IsSpace(*start))
+ start++;
+ while (start < last - 1 && __Pyx__PyBytes_AsDouble_IsSpace(last[-1]))
+ last--;
+ length = last - start;
+ if (unlikely(length <= 0)) goto fallback;
+
+ // parse NaN / inf
+ value = __Pyx__PyBytes_AsDouble_inf_nan(start, length);
+ if (unlikely(value == -1.0)) goto fallback;
+ if (value != 0.0) return value;
+
+ // look for underscores
+ digits = 0;
+ for (i=0; i < length; digits += start[i++] != '_');
+
+ if (likely(digits == length)) {
+ value = PyOS_string_to_double(start, &end, NULL);
+ } else if (digits < 40) {
+ char number[40];
+ last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+ if (unlikely(!last)) goto fallback;
+ value = PyOS_string_to_double(number, &end, NULL);
+ } else {
+ char *number = (char*) PyMem_Malloc((digits + 1) * sizeof(char));
+ if (unlikely(!number)) goto fallback;
+ last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+ if (unlikely(!last)) {
+ PyMem_Free(number);
+ goto fallback;
+ }
+ value = PyOS_string_to_double(number, &end, NULL);
+ PyMem_Free(number);
+ }
+ if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+ return value;
+ }
+fallback:
+ return __Pyx_SlowPyString_AsDouble(obj);
+}
+
/////////////// PyNumberPow2.proto ///////////////
@@ -468,7 +964,7 @@
static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject *none, int inplace) {
// in CPython, 1<ob_digit;
+ if (intval == 0) {
+ // == 0 => Py_SIZE(pyval) == 0
+ {{return_compare('size', '0', c_op)}}
+ } else if (intval < 0) {
+ // < 0 => Py_SIZE(pyval) < 0
+ if (size >= 0)
+ {{return_false if op == 'Eq' else return_true}};
+ // both are negative => can use absolute values now.
+ intval = -intval;
+ size = -size;
+ } else {
+ // > 0 => Py_SIZE(pyval) > 0
+ if (size <= 0)
+ {{return_false if op == 'Eq' else return_true}};
+ }
+ // After checking that the sign is the same (and excluding 0), now compare the absolute values.
+ // When inlining, the C compiler should select exactly one line from this unrolled loop.
+ uintval = (unsigned long) intval;
+ {{for _size in range(4, 0, -1)}}
+#if PyLong_SHIFT * {{_size}} < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * {{_size}})) {
+ // The C integer value is between (PyLong_BASE ** _size) and MIN(PyLong_BASE ** _size, LONG_MAX).
+ unequal = (size != {{_size+1}}) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ {{for _i in range(1, _size+1)}} | (digits[{{_i}}] != ((uintval >> ({{_i}} * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)){{endfor}};
+ } else
+#endif
+ {{endfor}}
+ unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK));
+
+ {{return_compare('unequal', '0', c_op)}}
+ }
+ #endif
+
+ if (PyFloat_CheckExact({{pyval}})) {
+ const long {{'a' if order == 'CObj' else 'b'}} = intval;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ double {{ival}} = __pyx_PyFloat_AsDouble({{pyval}});
+#else
+ double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
+#endif
+ {{return_compare('(double)a', '(double)b', c_op)}}
+ }
+
+ return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
+ PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
+}
+
+
/////////////// PyIntBinop.proto ///////////////
+{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
#if !CYTHON_COMPILING_IN_PYPY
-static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/
+static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check); /*proto*/
#else
-#define __Pyx_PyInt_{{op}}{{order}}(op1, op2, intval, inplace) \
- {{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}})
+#define __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(op1, op2, intval, inplace, zerodivision_check) \
+ {{if op in ('Eq', 'Ne')}}{{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(PyObject_RichCompare(op1, op2, Py_{{op.upper()}}))
{{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
{{endif}}
#endif
@@ -534,19 +1124,36 @@
#if !CYTHON_COMPILING_IN_PYPY
{{py: from Cython.Utility import pylong_join }}
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
+{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
+{{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}}
+{{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}}
{{py: slot_name = {'TrueDivide': 'true_divide', 'FloorDivide': 'floor_divide'}.get(op, op.lower()) }}
+{{py: cfunc_name = '__Pyx_PyInt_%s%s%s' % ('' if ret_type.is_pyobject else 'Bool', op, order)}}
{{py:
c_op = {
- 'Add': '+', 'Subtract': '-', 'Remainder': '%', 'TrueDivide': '/', 'FloorDivide': '/',
+ 'Add': '+', 'Subtract': '-', 'Multiply': '*', 'Remainder': '%', 'TrueDivide': '/', 'FloorDivide': '/',
'Or': '|', 'Xor': '^', 'And': '&', 'Rshift': '>>', 'Lshift': '<<',
'Eq': '==', 'Ne': '!=',
}[op]
}}
+{{py:
+def zerodiv_check(operand, optype='integer', _is_mod=op == 'Remainder', _needs_check=(order == 'CObj' and c_op in '%/')):
+ return (((
+ 'if (unlikely(zerodivision_check && ((%s) == 0))) {'
+ ' PyErr_SetString(PyExc_ZeroDivisionError, "%s division%s by zero");'
+ ' return NULL;'
+ '}') % (operand, optype, ' or modulo' if _is_mod else '')
+ ) if _needs_check else '')
+}}
+
+static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
-static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) {
{{if op in ('Eq', 'Ne')}}
if (op1 == op2) {
- Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}};
+ {{return_true if op == 'Eq' else return_false}};
}
{{endif}}
@@ -557,12 +1164,13 @@
long x;
{{endif}}
long {{ival}} = PyInt_AS_LONG({{pyval}});
+ {{zerodiv_check('b')}}
{{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) {
- Py_RETURN_TRUE;
+ {{return_true}};
} else {
- Py_RETURN_FALSE;
+ {{return_false}};
}
{{elif c_op in '+-'}}
// adapted from intobject.c in Py2.7:
@@ -577,7 +1185,7 @@
x += ((x != 0) & ((x ^ b) < 0)) * b;
return PyInt_FromLong(x);
{{elif op == 'TrueDivide'}}
- if (8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= (1L << 53))) {
+ if (8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53))) {
return PyFloat_FromDouble((double)a / (double)b);
}
// let Python do the rounding
@@ -596,9 +1204,22 @@
}
return PyInt_FromLong(x);
{{elif op == 'Lshift'}}
- if (likely(a == (a << b) >> b)) {
+ if (likely(b < (long) (sizeof(long)*8) && a == (a << b) >> b) || !a) {
return PyInt_FromLong(a {{c_op}} b);
}
+ {{elif c_op == '*'}}
+#ifdef HAVE_LONG_LONG
+ if (sizeof(PY_LONG_LONG) > sizeof(long)) {
+ PY_LONG_LONG result = (PY_LONG_LONG)a {{c_op}} (PY_LONG_LONG)b;
+ return (result >= LONG_MIN && result <= LONG_MAX) ?
+ PyInt_FromLong((long)result) : PyLong_FromLongLong(result);
+ }
+#endif
+#if CYTHON_USE_TYPE_SLOTS
+ return PyInt_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
+#else
+ return PyNumber_{{op}}(op1, op2);
+#endif
{{else}}
// other operations are safe, no overflow
return PyInt_FromLong(a {{c_op}} b);
@@ -618,6 +1239,38 @@
{{endif}}
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}});
+ {{if c_op == '&'}}
+ // special case for &-ing arbitrarily large numbers with known single digit operands
+ if ((intval & PyLong_MASK) == intval) {
+ long result = 0;
+ if(likely(size)) {
+ result = intval & (likely(size>0) ? digits[0] : (PyLong_MASK - digits[0] + 1));
+ }
+ return PyLong_FromLong(result);
+ }
+ {{endif}}
+ // special cases for 0: + - * % / // | ^ & >> <<
+ if (unlikely(size == 0)) {
+ {{if order == 'CObj' and c_op in '%/'}}
+ // division by zero!
+ {{zerodiv_check('0')}}
+ {{elif order == 'CObj' and c_op in '+-|^>><<'}}
+ // x == x+0 == x-0 == x|0 == x^0 == x>>0 == x<<0
+ return __Pyx_NewRef(op1);
+ {{elif order == 'CObj' and c_op in '*&'}}
+ // 0 == x*0 == x&0
+ return __Pyx_NewRef(op2);
+ {{elif order == 'ObjC' and c_op in '+|^'}}
+ // x == 0+x == 0|x == 0^x
+ return __Pyx_NewRef(op2);
+ {{elif order == 'ObjC' and c_op == '-'}}
+ // -x == 0-x
+ return PyLong_FromLong(-intval);
+ {{elif order == 'ObjC' and (c_op in '*%&>><<' or op == 'FloorDivide')}}
+ // 0 == 0*x == 0%x == 0&x == 0>>x == 0< {{_size}} * PyLong_SHIFT{{if op == 'TrueDivide'}} && {{_size-1}} * PyLong_SHIFT < 53{{endif}}) {
+ if (8 * sizeof(long) - 1 > {{_size}} * PyLong_SHIFT{{if c_op == '*'}}+30{{endif}}{{if op == 'TrueDivide'}} && {{_size-1}} * PyLong_SHIFT < 53{{endif}}) {
{{ival}} = {{'-' if _case < 0 else ''}}(long) {{pylong_join(_size, 'digits')}};
break;
{{if op not in ('Eq', 'Ne', 'TrueDivide')}}
-#ifdef HAVE_LONG_LONG
- } else if (8 * sizeof(PY_LONG_LONG) - 1 > {{_size}} * PyLong_SHIFT) {
+ #ifdef HAVE_LONG_LONG
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > {{_size}} * PyLong_SHIFT{{if c_op == '*'}}+30{{endif}}) {
ll{{ival}} = {{'-' if _case < 0 else ''}}(PY_LONG_LONG) {{pylong_join(_size, 'digits', 'unsigned PY_LONG_LONG')}};
goto long_long;
-#endif
+ #endif
{{endif}}
}
// if size doesn't fit into a long or PY_LONG_LONG anymore, fall through to default
+ CYTHON_FALLTHROUGH;
{{endfor}}
{{endfor}}
{{if op in ('Eq', 'Ne')}}
#if PyLong_SHIFT < 30 && PyLong_SHIFT != 15
// unusual setup - your fault
- default: return PyLong_Type.tp_richcompare({{'op1, op2' if order == 'ObjC' else 'op2, op1'}}, Py_{{op.upper()}});
+ default: return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
+ PyLong_Type.tp_richcompare({{'op1, op2' if order == 'ObjC' else 'op2, op1'}}, Py_{{op.upper()}}));
#else
// too large for the long values we allow => definitely not equal
- default: Py_RETURN_{{'FALSE' if op == 'Eq' else 'TRUE'}};
+ default: {{return_false if op == 'Eq' else return_true}};
#endif
{{else}}
default: return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
@@ -657,18 +1312,26 @@
}
{{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) {
- Py_RETURN_TRUE;
+ {{return_true}};
} else {
- Py_RETURN_FALSE;
+ {{return_false}};
}
{{else}}
- {{if c_op == '%'}}
+ {{if c_op == '*'}}
+ (void)a; (void)b;
+ #ifdef HAVE_LONG_LONG
+ ll{{ival}} = {{ival}};
+ goto long_long;
+ #else
+ return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
+ #endif
+ {{elif c_op == '%'}}
// see ExprNodes.py :: mod_int_utility_code
x = a % b;
x += ((x != 0) & ((x ^ b) < 0)) * b;
{{elif op == 'TrueDivide'}}
- if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= (1L << 53)))
- || __Pyx_sst_abs(size) <= 52 / PyLong_SHIFT) {
+ if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53)))
+ || __Pyx_sst_abs(size) <= 52 / PyLong_SHIFT) {
return PyFloat_FromDouble((double)a / (double)b);
}
return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
@@ -685,12 +1348,12 @@
x = a {{c_op}} b;
{{if op == 'Lshift'}}
#ifdef HAVE_LONG_LONG
- if (unlikely(a != x >> b)) {
+ if (unlikely(!(b < (long) (sizeof(long)*8) && a == x >> b)) && a) {
ll{{ival}} = {{ival}};
goto long_long;
}
#else
- if (likely(a == x >> b)) /* execute return statement below */
+ if (likely(b < (long) (sizeof(long)*8) && a == x >> b) || !a) /* execute return statement below */
#endif
{{endif}}
{{endif}}
@@ -725,18 +1388,23 @@
}
#endif
- {{if c_op in '+-' or op in ('TrueDivide', 'Eq', 'Ne')}}
+ {{if c_op in '+-*' or op in ('TrueDivide', 'Eq', 'Ne')}}
if (PyFloat_CheckExact({{pyval}})) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ double {{ival}} = __pyx_PyFloat_AsDouble({{pyval}});
+#else
double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
+#endif
{{if op in ('Eq', 'Ne')}}
if ((double)a {{c_op}} (double)b) {
- Py_RETURN_TRUE;
+ {{return_true}};
} else {
- Py_RETURN_FALSE;
+ {{return_false}};
}
{{else}}
double result;
+ {{zerodiv_check('b', 'float')}}
// copied from floatobject.c in Py3.5:
PyFPE_START_PROTECT("{{op.lower() if not op.endswith('Divide') else 'divide'}}", return NULL)
result = ((double)a) {{c_op}} (double)b;
@@ -747,7 +1415,8 @@
{{endif}}
{{if op in ('Eq', 'Ne')}}
- return PyObject_RichCompare(op1, op2, Py_{{op.upper()}});
+ return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
+ PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
{{else}}
return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2);
{{endif}}
@@ -756,11 +1425,12 @@
/////////////// PyFloatBinop.proto ///////////////
+{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
#if !CYTHON_COMPILING_IN_PYPY
-static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/
+static {{c_ret_type}} __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check); /*proto*/
#else
-#define __Pyx_PyFloat_{{op}}{{order}}(op1, op2, floatval, inplace) \
- {{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}})
+#define __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(op1, op2, floatval, inplace, zerodivision_check) \
+ {{if op in ('Eq', 'Ne')}}{{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(PyObject_RichCompare(op1, op2, Py_{{op.upper()}}))
{{elif op == 'Divide'}}((inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2)))
{{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
{{endif}}
@@ -770,31 +1440,52 @@
#if !CYTHON_COMPILING_IN_PYPY
{{py: from Cython.Utility import pylong_join }}
+{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
+{{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}}
+{{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}}
{{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
+{{py: cfunc_name = '__Pyx_PyFloat_%s%s%s' % ('' if ret_type.is_pyobject else 'Bool', op, order) }}
{{py:
c_op = {
'Add': '+', 'Subtract': '-', 'TrueDivide': '/', 'Divide': '/', 'Remainder': '%',
'Eq': '==', 'Ne': '!=',
}[op]
}}
+{{py:
+def zerodiv_check(operand, _is_mod=op == 'Remainder', _needs_check=(order == 'CObj' and c_op in '%/')):
+ return (((
+ 'if (unlikely(zerodivision_check && ((%s) == 0.0))) {'
+ ' PyErr_SetString(PyExc_ZeroDivisionError, "float division%s by zero");'
+ ' return NULL;'
+ '}') % (operand, ' or modulo' if _is_mod else '')
+ ) if _needs_check else '')
+}}
-static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, CYTHON_UNUSED int inplace) {
+static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check) {
const double {{'a' if order == 'CObj' else 'b'}} = floatval;
double {{fval}}{{if op not in ('Eq', 'Ne')}}, result{{endif}};
+ // Prevent "unused" warnings.
+ (void)inplace; (void)zerodivision_check;
{{if op in ('Eq', 'Ne')}}
if (op1 == op2) {
- Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}};
+ {{return_true if op == 'Eq' else return_false}};
}
{{endif}}
if (likely(PyFloat_CheckExact({{pyval}}))) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {{fval}} = __pyx_PyFloat_AsDouble({{pyval}});
+#else
{{fval}} = PyFloat_AS_DOUBLE({{pyval}});
+#endif
+ {{zerodiv_check(fval)}}
} else
#if PY_MAJOR_VERSION < 3
if (likely(PyInt_CheckExact({{pyval}}))) {
{{fval}} = (double) PyInt_AS_LONG({{pyval}});
+ {{zerodiv_check(fval)}}
} else
#endif
@@ -803,7 +1494,7 @@
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) {
- case 0: {{fval}} = 0.0; break;
+ case 0: {{fval}} = 0.0; {{zerodiv_check(fval)}} break;
case -1: {{fval}} = -(double) digits[0]; break;
case 1: {{fval}} = (double) digits[0]; break;
{{for _size in (2, 3, 4)}}
@@ -812,7 +1503,7 @@
if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || ({{_size-1}} * PyLong_SHIFT < 53))) {
{{fval}} = (double) {{pylong_join(_size, 'digits')}};
// let CPython do its own float rounding from 2**53 on (max. consecutive integer in double float)
- if ((8 * sizeof(unsigned long) < 53) || ({{_size}} * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) {
+ if ((8 * sizeof(unsigned long) < 53) || ({{_size}} * PyLong_SHIFT < 53) || ({{fval}} < (double) ((PY_LONG_LONG)1 << 53))) {
if (size == {{-_size}})
{{fval}} = -{{fval}};
break;
@@ -823,21 +1514,29 @@
// check above. However, the number of digits that CPython uses for a given PyLong
// value is minimal, and together with the "(size-1) * SHIFT < 53" check above,
// this should make it safe.
+ CYTHON_FALLTHROUGH;
{{endfor}}
default:
- #else
- {
#endif
{{if op in ('Eq', 'Ne')}}
- return PyFloat_Type.tp_richcompare({{'op1, op2' if order == 'CObj' else 'op2, op1'}}, Py_{{op.upper()}});
+ return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
+ PyFloat_Type.tp_richcompare({{'op1, op2' if order == 'CObj' else 'op2, op1'}}, Py_{{op.upper()}}));
{{else}}
{{fval}} = PyLong_AsDouble({{pyval}});
if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL;
+ {{if zerodiv_check(fval)}}
+ #if !CYTHON_USE_PYLONG_INTERNALS
+ {{zerodiv_check(fval)}}
+ #endif
+ {{endif}}
{{endif}}
+ #if CYTHON_USE_PYLONG_INTERNALS
}
+ #endif
} else {
{{if op in ('Eq', 'Ne')}}
- return PyObject_RichCompare(op1, op2, Py_{{op.upper()}});
+ return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
+ PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
{{elif op == 'Divide'}}
return (inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2));
{{else}}
@@ -847,9 +1546,9 @@
{{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) {
- Py_RETURN_TRUE;
+ {{return_true}};
} else {
- Py_RETURN_FALSE;
+ {{return_false}};
}
{{else}}
// copied from floatobject.c in Py3.5:
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Overflow.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Overflow.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Overflow.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Overflow.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,7 +1,7 @@
/*
These functions provide integer arithmetic with integer checking. They do not
actually raise an exception when an overflow is detected, but rather set a bit
-in the overflow parameter. (This parameter may be re-used accross several
+in the overflow parameter. (This parameter may be re-used across several
arithmetic operations, so should be or-ed rather than assigned to.)
The implementation is divided into two parts, the signed and unsigned basecases,
@@ -20,10 +20,10 @@
/////////////// Common.proto ///////////////
static int __Pyx_check_twos_complement(void) {
- if (-1 != ~0) {
+ if ((-1 != ~0)) {
PyErr_SetString(PyExc_RuntimeError, "Two's complement required for overflow checks.");
return 1;
- } else if (sizeof(short) == sizeof(int)) {
+ } else if ((sizeof(short) == sizeof(int))) {
PyErr_SetString(PyExc_RuntimeError, "sizeof(short) < sizeof(int) required for overflow checks.");
return 1;
} else {
@@ -31,11 +31,11 @@
}
}
-#define __PYX_IS_UNSIGNED(type) (((type) -1) > 0)
-#define __PYX_SIGN_BIT(type) (((unsigned type) 1) << (sizeof(type) * 8 - 1))
-#define __PYX_HALF_MAX(type) (((type) 1) << (sizeof(type) * 8 - 2))
-#define __PYX_MIN(type) (__PYX_IS_UNSIGNED(type) ? (type) 0 : 0 - __PYX_HALF_MAX(type) - __PYX_HALF_MAX(type))
-#define __PYX_MAX(type) (~__PYX_MIN(type))
+#define __PYX_IS_UNSIGNED(type) ((((type) -1) > 0))
+#define __PYX_SIGN_BIT(type) ((((unsigned type) 1) << (sizeof(type) * 8 - 1)))
+#define __PYX_HALF_MAX(type) ((((type) 1) << (sizeof(type) * 8 - 2)))
+#define __PYX_MIN(type) ((__PYX_IS_UNSIGNED(type) ? (type) 0 : 0 - __PYX_HALF_MAX(type) - __PYX_HALF_MAX(type)))
+#define __PYX_MAX(type) ((~__PYX_MIN(type)))
#define __Pyx_add_no_overflow(a, b, overflow) ((a) + (b))
#define __Pyx_add_const_no_overflow(a, b, overflow) ((a) + (b))
@@ -46,12 +46,36 @@
#define __Pyx_div_no_overflow(a, b, overflow) ((a) / (b))
#define __Pyx_div_const_no_overflow(a, b, overflow) ((a) / (b))
+#if defined(__has_builtin)
+# if __has_builtin(__builtin_add_overflow) && !defined(__ibmxl__)
+# define __PYX_HAVE_BUILTIN_OVERFLOW
+# endif
+#elif defined(__GNUC__) && (__GNUC__ >= 5) && (!defined(__INTEL_COMPILER) || (__INTEL_COMPILER >= 1800))
+# define __PYX_HAVE_BUILTIN_OVERFLOW
+#endif
+
+#if defined(__GNUC__)
+# define __Pyx_is_constant(x) (__builtin_constant_p(x))
+#elif defined(__has_builtin)
+# if __has_builtin(__builtin_constant_p)
+# define __Pyx_is_constant(x) (__builtin_constant_p(x))
+# endif
+#else
+# define __Pyx_is_constant(x) (0)
+#endif
+
/////////////// Common.init ///////////////
+//@substitute: naming
-__Pyx_check_twos_complement();
+// FIXME: Propagate the error here instead of just printing it.
+if (unlikely(__Pyx_check_twos_complement())) {
+ PyErr_WriteUnraisable($module_cname);
+}
/////////////// BaseCaseUnsigned.proto ///////////////
+{{if UINT == "long long"}}#ifdef HAVE_LONG_LONG{{endif}}
+
static CYTHON_INLINE {{UINT}} __Pyx_add_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
static CYTHON_INLINE {{UINT}} __Pyx_sub_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
@@ -60,11 +84,41 @@
// Use these when b is known at compile time.
#define __Pyx_add_const_{{NAME}}_checking_overflow __Pyx_add_{{NAME}}_checking_overflow
#define __Pyx_sub_const_{{NAME}}_checking_overflow __Pyx_sub_{{NAME}}_checking_overflow
+#if defined(__PYX_HAVE_BUILTIN_OVERFLOW)
+#define __Pyx_mul_const_{{NAME}}_checking_overflow __Pyx_mul_{{NAME}}_checking_overflow
+#else
static CYTHON_INLINE {{UINT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} constant, int *overflow);
+#endif
#define __Pyx_div_const_{{NAME}}_checking_overflow __Pyx_div_{{NAME}}_checking_overflow
+{{if UINT == "long long"}}#endif{{endif}}
+
/////////////// BaseCaseUnsigned ///////////////
+{{if UINT == "long long"}}#ifdef HAVE_LONG_LONG{{endif}}
+
+#if defined(__PYX_HAVE_BUILTIN_OVERFLOW)
+
+static CYTHON_INLINE {{UINT}} __Pyx_add_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
+ {{UINT}} result;
+ *overflow |= __builtin_add_overflow(a, b, &result);
+ return result;
+}
+
+static CYTHON_INLINE {{UINT}} __Pyx_sub_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
+ {{UINT}} result;
+ *overflow |= __builtin_sub_overflow(a, b, &result);
+ return result;
+}
+
+static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
+ {{UINT}} result;
+ *overflow |= __builtin_mul_overflow(a, b, &result);
+ return result;
+}
+
+#else
+
static CYTHON_INLINE {{UINT}} __Pyx_add_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
{{UINT}} r = a + b;
*overflow |= r < a;
@@ -78,34 +132,45 @@
}
static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
- if (sizeof({{UINT}}) < sizeof(unsigned long)) {
+ // if we have a constant, use the constant version
+ if (__Pyx_is_constant(b)) {
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(a, b, overflow);
+ } else if (__Pyx_is_constant(a)) {
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(b, a, overflow);
+ } else if ((sizeof({{UINT}}) < sizeof(unsigned long))) {
unsigned long big_r = ((unsigned long) a) * ((unsigned long) b);
{{UINT}} r = ({{UINT}}) big_r;
*overflow |= big_r != r;
return r;
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{UINT}}) < sizeof(unsigned PY_LONG_LONG)) {
+ } else if ((sizeof({{UINT}}) < sizeof(unsigned PY_LONG_LONG))) {
unsigned PY_LONG_LONG big_r = ((unsigned PY_LONG_LONG) a) * ((unsigned PY_LONG_LONG) b);
{{UINT}} r = ({{UINT}}) big_r;
*overflow |= big_r != r;
return r;
#endif
} else {
- {{UINT}} prod = a * b;
- double dprod = ((double) a) * ((double) b);
- // Overflow results in an error of at least 2^sizeof(UINT),
- // whereas rounding represents an error on the order of 2^(sizeof(UINT)-53).
- *overflow |= fabs(dprod - prod) > (__PYX_MAX({{UINT}}) / 2);
- return prod;
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(a, b, overflow);
}
}
static CYTHON_INLINE {{UINT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
- if (b > 1) {
- *overflow |= a > __PYX_MAX({{UINT}}) / b;
- }
- return a * b;
+ // note that deliberately the overflow check is written such that it divides by b; this
+ // function is used when b is a constant thus the compiler should be able to eliminate the
+ // (very slow on most CPUs!) division operation
+ {{UINT}} prod;
+ if (__Pyx_is_constant(a) && !__Pyx_is_constant(b)) {
+ // if a is a compile-time constant and b isn't, swap them
+ {{UINT}} temp = b;
+ b = a;
+ a = temp;
+ }
+ prod = a * b;
+ if (b != 0)
+ *overflow |= a > (__PYX_MAX({{UINT}}) / b);
+ return prod;
}
+#endif // __PYX_HAVE_BUILTIN_OVERFLOW
static CYTHON_INLINE {{UINT}} __Pyx_div_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
@@ -116,9 +181,13 @@
return a / b;
}
+{{if UINT == "long long"}}#endif{{endif}}
+
/////////////// BaseCaseSigned.proto ///////////////
+{{if INT == "long long"}}#ifdef HAVE_LONG_LONG{{endif}}
+
static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_sub_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
@@ -126,83 +195,110 @@
// Use when b is known at compile time.
-static CYTHON_INLINE {{INT}} __Pyx_add_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
-static CYTHON_INLINE {{INT}} __Pyx_sub_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
+#define __Pyx_add_const_{{NAME}}_checking_overflow __Pyx_add_{{NAME}}_checking_overflow
+#define __Pyx_sub_const_{{NAME}}_checking_overflow __Pyx_sub_{{NAME}}_checking_overflow
+#if defined(__PYX_HAVE_BUILTIN_OVERFLOW)
+#define __Pyx_mul_const_{{NAME}}_checking_overflow __Pyx_mul_{{NAME}}_checking_overflow
+#else
static CYTHON_INLINE {{INT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} constant, int *overflow);
+#endif
#define __Pyx_div_const_{{NAME}}_checking_overflow __Pyx_div_{{NAME}}_checking_overflow
+{{if INT == "long long"}}#endif{{endif}}
+
/////////////// BaseCaseSigned ///////////////
+{{if INT == "long long"}}#ifdef HAVE_LONG_LONG{{endif}}
+
+#if defined(__PYX_HAVE_BUILTIN_OVERFLOW)
+
+static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
+ {{INT}} result;
+ *overflow |= __builtin_add_overflow(a, b, &result);
+ return result;
+}
+
+static CYTHON_INLINE {{INT}} __Pyx_sub_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
+ {{INT}} result;
+ *overflow |= __builtin_sub_overflow(a, b, &result);
+ return result;
+}
+
+static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
+ {{INT}} result;
+ *overflow |= __builtin_mul_overflow(a, b, &result);
+ return result;
+}
+
+#else
+
static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
- if (sizeof({{INT}}) < sizeof(long)) {
+ if ((sizeof({{INT}}) < sizeof(long))) {
long big_r = ((long) a) + ((long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return r;
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{INT}}) < sizeof(PY_LONG_LONG)) {
+ } else if ((sizeof({{INT}}) < sizeof(PY_LONG_LONG))) {
PY_LONG_LONG big_r = ((PY_LONG_LONG) a) + ((PY_LONG_LONG) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return r;
#endif
} else {
- // Signed overflow undefined, but unsigned overflow is well defined.
- {{INT}} r = ({{INT}}) ((unsigned {{INT}}) a + (unsigned {{INT}}) b);
+ // Signed overflow undefined, but unsigned overflow is well defined. Casting is
+ // implementation-defined, but we assume two's complement (see __Pyx_check_twos_complement
+ // above), and arithmetic in two's-complement is the same as unsigned arithmetic.
+ unsigned {{INT}} r = (unsigned {{INT}}) a + (unsigned {{INT}}) b;
// Overflow happened if the operands have the same sign, but the result
// has opposite sign.
- // sign(a) == sign(b) != sign(r)
- {{INT}} sign_a = __PYX_SIGN_BIT({{INT}}) & a;
- {{INT}} sign_b = __PYX_SIGN_BIT({{INT}}) & b;
- {{INT}} sign_r = __PYX_SIGN_BIT({{INT}}) & r;
- *overflow |= (sign_a == sign_b) & (sign_a != sign_r);
- return r;
- }
-}
-
-static CYTHON_INLINE {{INT}} __Pyx_add_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
- if (b > 0) {
- *overflow |= a > __PYX_MAX({{INT}}) - b;
- } else if (b < 0) {
- *overflow |= a < __PYX_MIN({{INT}}) - b;
+ *overflow |= (((unsigned {{INT}})a ^ r) & ((unsigned {{INT}})b ^ r)) >> (8 * sizeof({{INT}}) - 1);
+ return ({{INT}}) r;
}
- return a + b;
}
static CYTHON_INLINE {{INT}} __Pyx_sub_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
- *overflow |= b == __PYX_MIN({{INT}});
- return __Pyx_add_{{NAME}}_checking_overflow(a, -b, overflow);
-}
-
-static CYTHON_INLINE {{INT}} __Pyx_sub_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
- *overflow |= b == __PYX_MIN({{INT}});
- return __Pyx_add_const_{{NAME}}_checking_overflow(a, -b, overflow);
+ // Compilers don't handle widening as well in the subtraction case, so don't bother
+ unsigned {{INT}} r = (unsigned {{INT}}) a - (unsigned {{INT}}) b;
+ // Overflow happened if the operands differing signs, and the result
+ // has opposite sign to a.
+ *overflow |= (((unsigned {{INT}})a ^ (unsigned {{INT}})b) & ((unsigned {{INT}})a ^ r)) >> (8 * sizeof({{INT}}) - 1);
+ return ({{INT}}) r;
}
static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
- if (sizeof({{INT}}) < sizeof(long)) {
+ // if we have a constant, use the constant version
+ if (__Pyx_is_constant(b)) {
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(a, b, overflow);
+ } else if (__Pyx_is_constant(a)) {
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(b, a, overflow);
+ } else if ((sizeof({{INT}}) < sizeof(long))) {
long big_r = ((long) a) * ((long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{INT}}) < sizeof(PY_LONG_LONG)) {
+ } else if ((sizeof({{INT}}) < sizeof(PY_LONG_LONG))) {
PY_LONG_LONG big_r = ((PY_LONG_LONG) a) * ((PY_LONG_LONG) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
#endif
} else {
- {{INT}} prod = a * b;
- double dprod = ((double) a) * ((double) b);
- // Overflow results in an error of at least 2^sizeof(INT),
- // whereas rounding represents an error on the order of 2^(sizeof(INT)-53).
- *overflow |= fabs(dprod - prod) > (__PYX_MAX({{INT}}) / 2);
- return prod;
+ return __Pyx_mul_const_{{NAME}}_checking_overflow(a, b, overflow);
}
}
static CYTHON_INLINE {{INT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
+ // note that deliberately all these comparisons are written such that they divide by b; this
+ // function is used when b is a constant thus the compiler should be able to eliminate the
+ // (very slow on most CPUs!) division operations
+ if (__Pyx_is_constant(a) && !__Pyx_is_constant(b)) {
+ // if a is a compile-time constant and b isn't, swap them
+ {{INT}} temp = b;
+ b = a;
+ a = temp;
+ }
if (b > 1) {
*overflow |= a > __PYX_MAX({{INT}}) / b;
*overflow |= a < __PYX_MIN({{INT}}) / b;
@@ -212,31 +308,38 @@
*overflow |= a > __PYX_MIN({{INT}}) / b;
*overflow |= a < __PYX_MAX({{INT}}) / b;
}
- return a * b;
+ return ({{INT}}) (((unsigned {{INT}})a) * ((unsigned {{INT}}) b));
}
+#endif // defined(__PYX_HAVE_BUILTIN_OVERFLOW)
static CYTHON_INLINE {{INT}} __Pyx_div_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (b == 0) {
*overflow |= 1;
return 0;
}
- *overflow |= (a == __PYX_MIN({{INT}})) & (b == -1);
- return a / b;
+ *overflow |= a == __PYX_MIN({{INT}}) && b == -1;
+ return ({{INT}}) ((unsigned {{INT}}) a / (unsigned {{INT}}) b);
}
+{{if INT == "long long"}}#endif{{endif}}
+
/////////////// SizeCheck.init ///////////////
+//@substitute: naming
-__Pyx_check_sane_{{NAME}}();
+// FIXME: Propagate the error here instead of just printing it.
+if (unlikely(__Pyx_check_sane_{{NAME}}())) {
+ PyErr_WriteUnraisable($module_cname);
+}
/////////////// SizeCheck.proto ///////////////
static int __Pyx_check_sane_{{NAME}}(void) {
- if (sizeof({{TYPE}}) <= sizeof(int) ||
+ if (((sizeof({{TYPE}}) <= sizeof(int)) ||
#ifdef HAVE_LONG_LONG
- sizeof({{TYPE}}) == sizeof(PY_LONG_LONG) ||
+ (sizeof({{TYPE}}) == sizeof(PY_LONG_LONG)) ||
#endif
- sizeof({{TYPE}}) == sizeof(long)) {
+ (sizeof({{TYPE}}) == sizeof(long)))) {
return 0;
} else {
PyErr_Format(PyExc_RuntimeError, \
@@ -253,31 +356,31 @@
/////////////// Binop ///////////////
static CYTHON_INLINE {{TYPE}} __Pyx_{{BINOP}}_{{NAME}}_checking_overflow({{TYPE}} a, {{TYPE}} b, int *overflow) {
- if (sizeof({{TYPE}}) < sizeof(int)) {
+ if ((sizeof({{TYPE}}) < sizeof(int))) {
return __Pyx_{{BINOP}}_no_overflow(a, b, overflow);
} else if (__PYX_IS_UNSIGNED({{TYPE}})) {
- if (sizeof({{TYPE}}) == sizeof(unsigned int)) {
- return __Pyx_{{BINOP}}_unsigned_int_checking_overflow(a, b, overflow);
- } else if (sizeof({{TYPE}}) == sizeof(unsigned long)) {
- return __Pyx_{{BINOP}}_unsigned_long_checking_overflow(a, b, overflow);
+ if ((sizeof({{TYPE}}) == sizeof(unsigned int))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_unsigned_int_checking_overflow(a, b, overflow);
+ } else if ((sizeof({{TYPE}}) == sizeof(unsigned long))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_unsigned_long_checking_overflow(a, b, overflow);
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{TYPE}}) == sizeof(unsigned PY_LONG_LONG)) {
- return __Pyx_{{BINOP}}_unsigned_long_long_checking_overflow(a, b, overflow);
+ } else if ((sizeof({{TYPE}}) == sizeof(unsigned PY_LONG_LONG))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_unsigned_long_long_checking_overflow(a, b, overflow);
#endif
} else {
- abort(); return 0; // handled elsewhere
+ abort(); return 0; /* handled elsewhere */
}
} else {
- if (sizeof({{TYPE}}) == sizeof(int)) {
- return __Pyx_{{BINOP}}_int_checking_overflow(a, b, overflow);
- } else if (sizeof({{TYPE}}) == sizeof(long)) {
- return __Pyx_{{BINOP}}_long_checking_overflow(a, b, overflow);
+ if ((sizeof({{TYPE}}) == sizeof(int))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_int_checking_overflow(a, b, overflow);
+ } else if ((sizeof({{TYPE}}) == sizeof(long))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_long_checking_overflow(a, b, overflow);
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{TYPE}}) == sizeof(PY_LONG_LONG)) {
- return __Pyx_{{BINOP}}_long_long_checking_overflow(a, b, overflow);
+ } else if ((sizeof({{TYPE}}) == sizeof(PY_LONG_LONG))) {
+ return ({{TYPE}}) __Pyx_{{BINOP}}_long_long_checking_overflow(a, b, overflow);
#endif
} else {
- abort(); return 0; // handled elsewhere
+ abort(); return 0; /* handled elsewhere */
}
}
}
@@ -285,19 +388,24 @@
/////////////// LeftShift.proto ///////////////
static CYTHON_INLINE {{TYPE}} __Pyx_lshift_{{NAME}}_checking_overflow({{TYPE}} a, {{TYPE}} b, int *overflow) {
- *overflow |=
+ int overflow_check =
#if {{SIGNED}}
- (b < 0) |
+ (a < 0) || (b < 0) ||
#endif
- (b > ({{TYPE}}) (8 * sizeof({{TYPE}}))) | (a > (__PYX_MAX({{TYPE}}) >> b));
- return a << b;
+ // the following must be a _logical_ OR as the RHS is undefined if the LHS is true
+ (b >= ({{TYPE}}) (8 * sizeof({{TYPE}}))) || (a > (__PYX_MAX({{TYPE}}) >> b));
+ if (overflow_check) {
+ *overflow |= 1;
+ return 0;
+ } else {
+ return a << b;
+ }
}
#define __Pyx_lshift_const_{{NAME}}_checking_overflow __Pyx_lshift_{{NAME}}_checking_overflow
/////////////// UnaryNegOverflows.proto ///////////////
-//FIXME: shouldn't the macro name be prefixed by "__Pyx_" ? Too late now, I guess...
// from intobject.c
-#define UNARY_NEG_WOULD_OVERFLOW(x) \
+#define __Pyx_UNARY_NEG_WOULD_OVERFLOW(x) \
(((x) < 0) & ((unsigned long)(x) == 0-(unsigned long)(x)))
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/Profile.c cython-0.20.1+1~202203241016-9537/Cython/Utility/Profile.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/Profile.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/Profile.c 2022-03-24 10:16:46.000000000 +0000
@@ -1,11 +1,12 @@
/////////////// Profile.proto ///////////////
+//@requires: Exceptions.c::PyErrFetchRestore
//@substitute: naming
// Note that cPython ignores PyTrace_EXCEPTION,
// but maybe some other profilers don't.
#ifndef CYTHON_PROFILE
-#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_PYSTON
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
#define CYTHON_PROFILE 0
#else
#define CYTHON_PROFILE 1
@@ -37,6 +38,12 @@
#include "compile.h"
#include "frameobject.h"
#include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
#if CYTHON_PROFILE_REUSE_FRAME
#define CYTHON_FRAME_MODIFIER static
@@ -46,13 +53,57 @@
#define CYTHON_FRAME_DEL(frame) Py_CLEAR(frame)
#endif
- #define __Pyx_TraceDeclarations \
- static PyCodeObject *$frame_code_cname = NULL; \
- CYTHON_FRAME_MODIFIER PyFrameObject *$frame_cname = NULL; \
- int __Pyx_use_tracing = 0;
+ #define __Pyx_TraceDeclarations \
+ static PyCodeObject *$frame_code_cname = NULL; \
+ CYTHON_FRAME_MODIFIER PyFrameObject *$frame_cname = NULL; \
+ int __Pyx_use_tracing = 0;
+
+ #define __Pyx_TraceFrameInit(codeobj) \
+ if (codeobj) $frame_code_cname = (PyCodeObject*) codeobj;
+
+#if PY_VERSION_HEX >= 0x030b00a2
+ #define __Pyx_IsTracing(tstate, check_tracing, check_funcs) \
+ (unlikely((tstate)->cframe->use_tracing) && \
+ (!(check_tracing) || !(tstate)->tracing) && \
+ (!(check_funcs) || (tstate)->c_profilefunc || (CYTHON_TRACE && (tstate)->c_tracefunc)))
+
+ #define __Pyx_EnterTracing(tstate) PyThreadState_EnterTracing(tstate)
+
+ #define __Pyx_LeaveTracing(tstate) PyThreadState_LeaveTracing(tstate)
+
+#elif PY_VERSION_HEX >= 0x030a00b1
+ #define __Pyx_IsTracing(tstate, check_tracing, check_funcs) \
+ (unlikely((tstate)->cframe->use_tracing) && \
+ (!(check_tracing) || !(tstate)->tracing) && \
+ (!(check_funcs) || (tstate)->c_profilefunc || (CYTHON_TRACE && (tstate)->c_tracefunc)))
+
+ #define __Pyx_EnterTracing(tstate) \
+ do { tstate->tracing++; tstate->cframe->use_tracing = 0; } while (0)
+
+ #define __Pyx_LeaveTracing(tstate) \
+ do { \
+ tstate->tracing--; \
+ tstate->cframe->use_tracing = ((CYTHON_TRACE && tstate->c_tracefunc != NULL) \
+ || tstate->c_profilefunc != NULL); \
+ } while (0)
- #define __Pyx_TraceFrameInit(codeobj) \
- if (codeobj) $frame_code_cname = (PyCodeObject*) codeobj;
+#else
+ #define __Pyx_IsTracing(tstate, check_tracing, check_funcs) \
+ (unlikely((tstate)->use_tracing) && \
+ (!(check_tracing) || !(tstate)->tracing) && \
+ (!(check_funcs) || (tstate)->c_profilefunc || (CYTHON_TRACE && (tstate)->c_tracefunc)))
+
+ #define __Pyx_EnterTracing(tstate) \
+ do { tstate->tracing++; tstate->use_tracing = 0; } while (0)
+
+ #define __Pyx_LeaveTracing(tstate) \
+ do { \
+ tstate->tracing--; \
+ tstate->use_tracing = ((CYTHON_TRACE && tstate->c_tracefunc != NULL) \
+ || tstate->c_profilefunc != NULL); \
+ } while (0)
+
+#endif
#ifdef WITH_THREAD
#define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error) \
@@ -60,28 +111,25 @@
if (CYTHON_TRACE_NOGIL) { \
PyThreadState *tstate; \
PyGILState_STATE state = PyGILState_Ensure(); \
- tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing) && !tstate->tracing && \
- (tstate->c_profilefunc || (CYTHON_TRACE && tstate->c_tracefunc))) { \
- __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, funcname, srcfile, firstlineno); \
+ tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 1, 1)) { \
+ __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, tstate, funcname, srcfile, firstlineno); \
} \
PyGILState_Release(state); \
if (unlikely(__Pyx_use_tracing < 0)) goto_error; \
} \
} else { \
PyThreadState* tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing) && !tstate->tracing && \
- (tstate->c_profilefunc || (CYTHON_TRACE && tstate->c_tracefunc))) { \
- __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, funcname, srcfile, firstlineno); \
+ if (__Pyx_IsTracing(tstate, 1, 1)) { \
+ __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, tstate, funcname, srcfile, firstlineno); \
if (unlikely(__Pyx_use_tracing < 0)) goto_error; \
} \
}
#else
#define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error) \
{ PyThreadState* tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing) && !tstate->tracing && \
- (tstate->c_profilefunc || (CYTHON_TRACE && tstate->c_tracefunc))) { \
- __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, funcname, srcfile, firstlineno); \
+ if (__Pyx_IsTracing(tstate, 1, 1)) { \
+ __Pyx_use_tracing = __Pyx_TraceSetupAndCall(&$frame_code_cname, &$frame_cname, tstate, funcname, srcfile, firstlineno); \
if (unlikely(__Pyx_use_tracing < 0)) goto_error; \
} \
}
@@ -89,11 +137,9 @@
#define __Pyx_TraceException() \
if (likely(!__Pyx_use_tracing)); else { \
- PyThreadState* tstate = PyThreadState_GET(); \
- if (tstate->use_tracing && \
- (tstate->c_profilefunc || (CYTHON_TRACE && tstate->c_tracefunc))) { \
- tstate->tracing++; \
- tstate->use_tracing = 0; \
+ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 1)) { \
+ __Pyx_EnterTracing(tstate); \
PyObject *exc_info = __Pyx_GetExceptionTuple(tstate); \
if (exc_info) { \
if (CYTHON_TRACE && tstate->c_tracefunc) \
@@ -103,24 +149,21 @@
tstate->c_profileobj, $frame_cname, PyTrace_EXCEPTION, exc_info); \
Py_DECREF(exc_info); \
} \
- tstate->use_tracing = 1; \
- tstate->tracing--; \
+ __Pyx_LeaveTracing(tstate); \
} \
}
static void __Pyx_call_return_trace_func(PyThreadState *tstate, PyFrameObject *frame, PyObject *result) {
PyObject *type, *value, *traceback;
- PyErr_Fetch(&type, &value, &traceback);
- tstate->tracing++;
- tstate->use_tracing = 0;
+ __Pyx_ErrFetchInState(tstate, &type, &value, &traceback);
+ __Pyx_EnterTracing(tstate);
if (CYTHON_TRACE && tstate->c_tracefunc)
tstate->c_tracefunc(tstate->c_traceobj, frame, PyTrace_RETURN, result);
if (tstate->c_profilefunc)
tstate->c_profilefunc(tstate->c_profileobj, frame, PyTrace_RETURN, result);
CYTHON_FRAME_DEL(frame);
- tstate->use_tracing = 1;
- tstate->tracing--;
- PyErr_Restore(type, value, traceback);
+ __Pyx_LeaveTracing(tstate);
+ __Pyx_ErrRestoreInState(tstate, type, value, traceback);
}
#ifdef WITH_THREAD
@@ -130,15 +173,15 @@
if (CYTHON_TRACE_NOGIL) { \
PyThreadState *tstate; \
PyGILState_STATE state = PyGILState_Ensure(); \
- tstate = PyThreadState_GET(); \
- if (tstate->use_tracing) { \
+ tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0)) { \
__Pyx_call_return_trace_func(tstate, $frame_cname, (PyObject*)result); \
} \
PyGILState_Release(state); \
} \
} else { \
- PyThreadState* tstate = PyThreadState_GET(); \
- if (tstate->use_tracing) { \
+ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0)) { \
__Pyx_call_return_trace_func(tstate, $frame_cname, (PyObject*)result); \
} \
} \
@@ -146,22 +189,22 @@
#else
#define __Pyx_TraceReturn(result, nogil) \
if (likely(!__Pyx_use_tracing)); else { \
- PyThreadState* tstate = PyThreadState_GET(); \
- if (tstate->use_tracing) { \
+ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0)) { \
__Pyx_call_return_trace_func(tstate, $frame_cname, (PyObject*)result); \
} \
}
#endif
static PyCodeObject *__Pyx_createFrameCodeObject(const char *funcname, const char *srcfile, int firstlineno); /*proto*/
- static int __Pyx_TraceSetupAndCall(PyCodeObject** code, PyFrameObject** frame, const char *funcname, const char *srcfile, int firstlineno); /*proto*/
+ static int __Pyx_TraceSetupAndCall(PyCodeObject** code, PyFrameObject** frame, PyThreadState* tstate, const char *funcname, const char *srcfile, int firstlineno); /*proto*/
#else
#define __Pyx_TraceDeclarations
#define __Pyx_TraceFrameInit(codeobj)
// mark error label as used to avoid compiler warnings
- #define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error) if (1); else goto_error;
+ #define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error) if ((1)); else goto_error;
#define __Pyx_TraceException()
#define __Pyx_TraceReturn(result, nogil)
@@ -172,15 +215,15 @@
static int __Pyx_call_line_trace_func(PyThreadState *tstate, PyFrameObject *frame, int lineno) {
int ret;
PyObject *type, *value, *traceback;
- PyErr_Fetch(&type, &value, &traceback);
+ __Pyx_ErrFetchInState(tstate, &type, &value, &traceback);
__Pyx_PyFrame_SetLineNumber(frame, lineno);
- tstate->tracing++;
- tstate->use_tracing = 0;
+ __Pyx_EnterTracing(tstate);
+
ret = tstate->c_tracefunc(tstate->c_traceobj, frame, PyTrace_LINE, NULL);
- tstate->use_tracing = 1;
- tstate->tracing--;
+
+ __Pyx_LeaveTracing(tstate);
if (likely(!ret)) {
- PyErr_Restore(type, value, traceback);
+ __Pyx_ErrRestoreInState(tstate, type, value, traceback);
} else {
Py_XDECREF(type);
Py_XDECREF(value);
@@ -196,17 +239,17 @@
if (CYTHON_TRACE_NOGIL) { \
int ret = 0; \
PyThreadState *tstate; \
- PyGILState_STATE state = PyGILState_Ensure(); \
- tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \
+ PyGILState_STATE state = __Pyx_PyGILState_Ensure(); \
+ tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0) && tstate->c_tracefunc && $frame_cname->f_trace) { \
ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
} \
- PyGILState_Release(state); \
+ __Pyx_PyGILState_Release(state); \
if (unlikely(ret)) goto_error; \
} \
} else { \
- PyThreadState* tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \
+ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0) && tstate->c_tracefunc && $frame_cname->f_trace) { \
int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
if (unlikely(ret)) goto_error; \
} \
@@ -215,8 +258,8 @@
#else
#define __Pyx_TraceLine(lineno, nogil, goto_error) \
if (likely(!__Pyx_use_tracing)); else { \
- PyThreadState* tstate = PyThreadState_GET(); \
- if (unlikely(tstate->use_tracing && tstate->c_tracefunc)) { \
+ PyThreadState* tstate = __Pyx_PyThreadState_Current; \
+ if (__Pyx_IsTracing(tstate, 0, 0) && tstate->c_tracefunc && $frame_cname->f_trace) { \
int ret = __Pyx_call_line_trace_func(tstate, $frame_cname, lineno); \
if (unlikely(ret)) goto_error; \
} \
@@ -224,7 +267,7 @@
#endif
#else
// mark error label as used to avoid compiler warnings
- #define __Pyx_TraceLine(lineno, nogil, goto_error) if (1); else goto_error;
+ #define __Pyx_TraceLine(lineno, nogil, goto_error) if ((1)); else goto_error;
#endif
/////////////// Profile ///////////////
@@ -234,12 +277,12 @@
static int __Pyx_TraceSetupAndCall(PyCodeObject** code,
PyFrameObject** frame,
+ PyThreadState* tstate,
const char *funcname,
const char *srcfile,
int firstlineno) {
PyObject *type, *value, *traceback;
int retval;
- PyThreadState* tstate = PyThreadState_GET();
if (*frame == NULL || !CYTHON_PROFILE_REUSE_FRAME) {
if (*code == NULL) {
*code = __Pyx_createFrameCodeObject(funcname, srcfile, firstlineno);
@@ -262,23 +305,23 @@
(*frame)->f_tstate = tstate;
#endif
}
- __Pyx_PyFrame_SetLineNumber(*frame, firstlineno);
+ __Pyx_PyFrame_SetLineNumber(*frame, firstlineno);
+
retval = 1;
- tstate->tracing++;
- tstate->use_tracing = 0;
- PyErr_Fetch(&type, &value, &traceback);
+ __Pyx_EnterTracing(tstate);
+ __Pyx_ErrFetchInState(tstate, &type, &value, &traceback);
+
#if CYTHON_TRACE
if (tstate->c_tracefunc)
retval = tstate->c_tracefunc(tstate->c_traceobj, *frame, PyTrace_CALL, NULL) == 0;
if (retval && tstate->c_profilefunc)
#endif
retval = tstate->c_profilefunc(tstate->c_profileobj, *frame, PyTrace_CALL, NULL) == 0;
- tstate->use_tracing = (tstate->c_profilefunc ||
- (CYTHON_TRACE && tstate->c_tracefunc));
- tstate->tracing--;
+
+ __Pyx_LeaveTracing(tstate);
if (retval) {
- PyErr_Restore(type, value, traceback);
- return tstate->use_tracing && retval;
+ __Pyx_ErrRestoreInState(tstate, type, value, traceback);
+ return __Pyx_IsTracing(tstate, 0, 0) && retval;
} else {
Py_XDECREF(type);
Py_XDECREF(value);
@@ -288,27 +331,29 @@
}
static PyCodeObject *__Pyx_createFrameCodeObject(const char *funcname, const char *srcfile, int firstlineno) {
+ PyCodeObject *py_code = 0;
+
+#if PY_MAJOR_VERSION >= 3
+ py_code = PyCode_NewEmpty(srcfile, funcname, firstlineno);
+ // make CPython use a fresh dict for "f_locals" at need (see GH #1836)
+ if (likely(py_code)) {
+ py_code->co_flags |= CO_OPTIMIZED | CO_NEWLOCALS;
+ }
+#else
PyObject *py_srcfile = 0;
PyObject *py_funcname = 0;
- PyCodeObject *py_code = 0;
- #if PY_MAJOR_VERSION < 3
py_funcname = PyString_FromString(funcname);
+ if (unlikely(!py_funcname)) goto bad;
py_srcfile = PyString_FromString(srcfile);
- #else
- py_funcname = PyUnicode_FromString(funcname);
- py_srcfile = PyUnicode_FromString(srcfile);
- #endif
- if (!py_funcname | !py_srcfile) goto bad;
+ if (unlikely(!py_srcfile)) goto bad;
py_code = PyCode_New(
0, /*int argcount,*/
- #if PY_MAJOR_VERSION >= 3
- 0, /*int kwonlyargcount,*/
- #endif
0, /*int nlocals,*/
0, /*int stacksize,*/
- 0, /*int flags,*/
+ // make CPython use a fresh dict for "f_locals" at need (see GH #1836)
+ CO_OPTIMIZED | CO_NEWLOCALS, /*int flags,*/
$empty_bytes, /*PyObject *code,*/
$empty_tuple, /*PyObject *consts,*/
$empty_tuple, /*PyObject *names,*/
@@ -324,6 +369,7 @@
bad:
Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname);
+#endif
return py_code;
}
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/StringTools.c cython-0.20.1+1~202203241016-9537/Cython/Utility/StringTools.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/StringTools.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/StringTools.c 2022-03-24 10:16:46.000000000 +0000
@@ -7,15 +7,78 @@
#include
+
+//////////////////// ssize_strlen.proto ////////////////////
+
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s);/*proto*/
+
+//////////////////// ssize_strlen ////////////////////
+//@requires: IncludeStringH
+
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) {
+ size_t len = strlen(s);
+ if (unlikely(len > PY_SSIZE_T_MAX)) {
+ PyErr_SetString(PyExc_OverflowError, "byte string is too long");
+ return -1;
+ }
+ return (Py_ssize_t) len;
+}
+
+
+//////////////////// ssize_pyunicode_strlen.proto ////////////////////
+
+static CYTHON_INLINE Py_ssize_t __Pyx_Py_UNICODE_ssize_strlen(const Py_UNICODE *u);/*proto*/
+
+//////////////////// ssize_pyunicode_strlen ////////////////////
+
+static CYTHON_INLINE Py_ssize_t __Pyx_Py_UNICODE_ssize_strlen(const Py_UNICODE *u) {
+ size_t len = __Pyx_Py_UNICODE_strlen(u);
+ if (unlikely(len > PY_SSIZE_T_MAX)) {
+ PyErr_SetString(PyExc_OverflowError, "Py_UNICODE string is too long");
+ return -1;
+ }
+ return (Py_ssize_t) len;
+}
+
+
//////////////////// InitStrings.proto ////////////////////
+#if CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_InitString(__Pyx_StringTabEntry t, PyObject **str); /*proto*/
+#else
static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/
+#endif
//////////////////// InitStrings ////////////////////
+#if PY_MAJOR_VERSION >= 3
+static int __Pyx_InitString(__Pyx_StringTabEntry t, PyObject **str) {
+ if (t.is_unicode | t.is_str) {
+ if (t.intern) {
+ *str = PyUnicode_InternFromString(t.s);
+ } else if (t.encoding) {
+ *str = PyUnicode_Decode(t.s, t.n - 1, t.encoding, NULL);
+ } else {
+ *str = PyUnicode_FromStringAndSize(t.s, t.n - 1);
+ }
+ } else {
+ *str = PyBytes_FromStringAndSize(t.s, t.n - 1);
+ }
+ if (!*str)
+ return -1;
+ // initialise cached hash value
+ if (PyObject_Hash(*str) == -1)
+ return -1;
+ return 0;
+}
+#endif
+
+#if !CYTHON_COMPILING_IN_LIMITED_API
static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) {
while (t->p) {
- #if PY_MAJOR_VERSION < 3
+ #if PY_MAJOR_VERSION >= 3 /* Python 3+ has unicode identifiers */
+ __Pyx_InitString(*t, t->p);
+ #else
if (t->is_unicode) {
*t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL);
} else if (t->intern) {
@@ -23,25 +86,17 @@
} else {
*t->p = PyString_FromStringAndSize(t->s, t->n - 1);
}
- #else /* Python 3+ has unicode identifiers */
- if (t->is_unicode | t->is_str) {
- if (t->intern) {
- *t->p = PyUnicode_InternFromString(t->s);
- } else if (t->encoding) {
- *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL);
- } else {
- *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1);
- }
- } else {
- *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1);
- }
- #endif
if (!*t->p)
return -1;
+ // initialise cached hash value
+ if (PyObject_Hash(*t->p) == -1)
+ return -1;
+ #endif
++t;
}
return 0;
}
+#endif
//////////////////// BytesContains.proto ////////////////////
@@ -60,14 +115,53 @@
//////////////////// PyUCS4InUnicode.proto ////////////////////
static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character); /*proto*/
-static CYTHON_INLINE int __Pyx_PyUnicodeBufferContainsUCS4(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character); /*proto*/
//////////////////// PyUCS4InUnicode ////////////////////
+#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE))
+
+#if PY_VERSION_HEX < 0x03090000
+#define __Pyx_PyUnicode_AS_UNICODE(op) PyUnicode_AS_UNICODE(op)
+#define __Pyx_PyUnicode_GET_SIZE(op) PyUnicode_GET_SIZE(op)
+#else
+// Avoid calling deprecated C-API functions in Py3.9+ that PEP-623 schedules for removal in Py3.12.
+// https://www.python.org/dev/peps/pep-0623/
+#define __Pyx_PyUnicode_AS_UNICODE(op) (((PyASCIIObject *)(op))->wstr)
+#define __Pyx_PyUnicode_GET_SIZE(op) ((PyCompactUnicodeObject *)(op))->wstr_length
+#endif
+
+#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2
+static int __Pyx_PyUnicodeBufferContainsUCS4_SP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) {
+ /* handle surrogate pairs for Py_UNICODE buffers in 16bit Unicode builds */
+ Py_UNICODE high_val, low_val;
+ Py_UNICODE* pos;
+ high_val = (Py_UNICODE) (0xD800 | (((character - 0x10000) >> 10) & ((1<<10)-1)));
+ low_val = (Py_UNICODE) (0xDC00 | ( (character - 0x10000) & ((1<<10)-1)));
+ for (pos=buffer; pos < buffer+length-1; pos++) {
+ if (unlikely((high_val == pos[0]) & (low_val == pos[1]))) return 1;
+ }
+ return 0;
+}
+#endif
+
+static int __Pyx_PyUnicodeBufferContainsUCS4_BMP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) {
+ Py_UNICODE uchar;
+ Py_UNICODE* pos;
+ uchar = (Py_UNICODE) character;
+ for (pos=buffer; pos < buffer+length; pos++) {
+ if (unlikely(uchar == pos[0])) return 1;
+ }
+ return 0;
+}
+#endif
+
static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character) {
#if CYTHON_PEP393_ENABLED
const int kind = PyUnicode_KIND(unicode);
- if (likely(kind != PyUnicode_WCHAR_KIND)) {
+ #ifdef PyUnicode_WCHAR_KIND
+ if (likely(kind != PyUnicode_WCHAR_KIND))
+ #endif
+ {
Py_ssize_t i;
const void* udata = PyUnicode_DATA(unicode);
const Py_ssize_t length = PyUnicode_GET_LENGTH(unicode);
@@ -76,33 +170,29 @@
}
return 0;
}
+#elif PY_VERSION_HEX >= 0x03090000
+ #error Cannot use "UChar in Unicode" in Python 3.9 without PEP-393 unicode strings.
+#elif !defined(PyUnicode_AS_UNICODE)
+ #error Cannot use "UChar in Unicode" in Python < 3.9 without Py_UNICODE support.
#endif
- return __Pyx_PyUnicodeBufferContainsUCS4(
- PyUnicode_AS_UNICODE(unicode),
- PyUnicode_GET_SIZE(unicode),
- character);
-}
-static CYTHON_INLINE int __Pyx_PyUnicodeBufferContainsUCS4(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) {
- Py_UNICODE uchar;
- Py_UNICODE* pos;
- #if Py_UNICODE_SIZE == 2
- if (character > 65535) {
- /* handle surrogate pairs for Py_UNICODE buffers in 16bit Unicode builds */
- Py_UNICODE high_val, low_val;
- high_val = (Py_UNICODE) (0xD800 | (((character - 0x10000) >> 10) & ((1<<10)-1)));
- low_val = (Py_UNICODE) (0xDC00 | ( (character - 0x10000) & ((1<<10)-1)));
- for (pos=buffer; pos < buffer+length-1; pos++) {
- if (unlikely(high_val == pos[0]) & unlikely(low_val == pos[1])) return 1;
- }
- return 0;
- }
- #endif
- uchar = (Py_UNICODE) character;
- for (pos=buffer; pos < buffer+length; pos++) {
- if (unlikely(uchar == pos[0])) return 1;
+#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE))
+#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2
+ if ((sizeof(Py_UNICODE) == 2) && unlikely(character > 65535)) {
+ return __Pyx_PyUnicodeBufferContainsUCS4_SP(
+ __Pyx_PyUnicode_AS_UNICODE(unicode),
+ __Pyx_PyUnicode_GET_SIZE(unicode),
+ character);
+ } else
+#endif
+ {
+ return __Pyx_PyUnicodeBufferContainsUCS4_BMP(
+ __Pyx_PyUnicode_AS_UNICODE(unicode),
+ __Pyx_PyUnicode_GET_SIZE(unicode),
+ character);
+
}
- return 0;
+#endif
}
@@ -145,7 +235,7 @@
//@requires: BytesEquals
static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) {
-#if CYTHON_COMPILING_IN_PYPY
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API
return PyObject_RichCompareBool(s1, s2, equals);
#else
#if PY_MAJOR_VERSION < 3
@@ -185,6 +275,21 @@
if (length != __Pyx_PyUnicode_GET_LENGTH(s2)) {
goto return_ne;
}
+#if CYTHON_USE_UNICODE_INTERNALS
+ {
+ Py_hash_t hash1, hash2;
+ #if CYTHON_PEP393_ENABLED
+ hash1 = ((PyASCIIObject*)s1)->hash;
+ hash2 = ((PyASCIIObject*)s2)->hash;
+ #else
+ hash1 = ((PyUnicodeObject*)s1)->hash;
+ hash2 = ((PyUnicodeObject*)s2)->hash;
+ #endif
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ goto return_ne;
+ }
+ }
+#endif
// len(s1) == len(s2) >= 1 (empty string is interned, and "s1 is not s2")
kind = __Pyx_PyUnicode_KIND(s1);
if (kind != __Pyx_PyUnicode_KIND(s2)) {
@@ -210,6 +315,9 @@
} else {
int result;
PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ #if PY_MAJOR_VERSION < 3
+ Py_XDECREF(owned_ref);
+ #endif
if (!py_result)
return -1;
result = __Pyx_PyObject_IsTrue(py_result);
@@ -238,7 +346,7 @@
//@requires: IncludeStringH
static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) {
-#if CYTHON_COMPILING_IN_PYPY
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API
return PyObject_RichCompareBool(s1, s2, equals);
#else
if (s1 == s2) {
@@ -257,7 +365,16 @@
} else if (length == 1) {
return (equals == Py_EQ);
} else {
- int result = memcmp(ps1, ps2, (size_t)length);
+ int result;
+#if CYTHON_USE_UNICODE_INTERNALS
+ Py_hash_t hash1, hash2;
+ hash1 = ((PyBytesObject*)s1)->ob_shash;
+ hash2 = ((PyBytesObject*)s2)->ob_shash;
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ return (equals == Py_NE);
+ }
+#endif
+ result = memcmp(ps1, ps2, (size_t)length);
return (equals == Py_EQ) ? (result == 0) : (result != 0);
}
} else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) {
@@ -294,7 +411,7 @@
if (wraparound | boundscheck) {
length = PyByteArray_GET_SIZE(string);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
return (unsigned char) (PyByteArray_AS_STRING(string)[i]);
} else {
PyErr_SetString(PyExc_IndexError, "bytearray index out of range");
@@ -324,7 +441,7 @@
if (wraparound | boundscheck) {
length = PyByteArray_GET_SIZE(string);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
PyByteArray_AS_STRING(string)[i] = (char) v;
return 0;
} else {
@@ -357,7 +474,7 @@
if (wraparound | boundscheck) {
length = __Pyx_PyUnicode_GET_LENGTH(ustring);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
return __Pyx_PyUnicode_READ_CHAR(ustring, i);
} else {
PyErr_SetString(PyExc_IndexError, "string index out of range");
@@ -369,6 +486,21 @@
}
+/////////////// decode_c_string_utf16.proto ///////////////
+
+static CYTHON_INLINE PyObject *__Pyx_PyUnicode_DecodeUTF16(const char *s, Py_ssize_t size, const char *errors) {
+ int byteorder = 0;
+ return PyUnicode_DecodeUTF16(s, size, errors, &byteorder);
+}
+static CYTHON_INLINE PyObject *__Pyx_PyUnicode_DecodeUTF16LE(const char *s, Py_ssize_t size, const char *errors) {
+ int byteorder = -1;
+ return PyUnicode_DecodeUTF16(s, size, errors, &byteorder);
+}
+static CYTHON_INLINE PyObject *__Pyx_PyUnicode_DecodeUTF16BE(const char *s, Py_ssize_t size, const char *errors) {
+ int byteorder = 1;
+ return PyUnicode_DecodeUTF16(s, size, errors, &byteorder);
+}
+
/////////////// decode_cpp_string.proto ///////////////
//@requires: IncludeCppStringH
//@requires: decode_c_bytes
@@ -390,6 +522,8 @@
/////////////// decode_c_string ///////////////
//@requires: IncludeStringH
+//@requires: decode_c_string_utf16
+//@substitute: naming
/* duplicate code to avoid calling strlen() if start >= 0 and stop >= 0 */
static CYTHON_INLINE PyObject* __Pyx_decode_c_string(
@@ -413,9 +547,9 @@
if (stop < 0)
stop += length;
}
+ if (unlikely(stop <= start))
+ return __Pyx_NewRef($empty_unicode);
length = stop - start;
- if (unlikely(length <= 0))
- return PyUnicode_FromUnicode(NULL, 0);
cstring += start;
if (decode_func) {
return decode_func(cstring, length, errors);
@@ -432,6 +566,8 @@
PyObject* (*decode_func)(const char *s, Py_ssize_t size, const char *errors));
/////////////// decode_c_bytes ///////////////
+//@requires: decode_c_string_utf16
+//@substitute: naming
static CYTHON_INLINE PyObject* __Pyx_decode_c_bytes(
const char* cstring, Py_ssize_t length, Py_ssize_t start, Py_ssize_t stop,
@@ -448,9 +584,9 @@
}
if (stop > length)
stop = length;
+ if (unlikely(stop <= start))
+ return __Pyx_NewRef($empty_unicode);
length = stop - start;
- if (unlikely(length <= 0))
- return PyUnicode_FromUnicode(NULL, 0);
cstring += start;
if (decode_func) {
return decode_func(cstring, length, errors);
@@ -489,6 +625,7 @@
PyObject* text, Py_ssize_t start, Py_ssize_t stop);
/////////////// PyUnicode_Substring ///////////////
+//@substitute: naming
static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring(
PyObject* text, Py_ssize_t start, Py_ssize_t stop) {
@@ -504,9 +641,10 @@
stop += length;
else if (stop > length)
stop = length;
- length = stop - start;
- if (length <= 0)
- return PyUnicode_FromUnicode(NULL, 0);
+ if (stop <= start)
+ return __Pyx_NewRef($empty_unicode);
+ if (start == 0 && stop == length)
+ return __Pyx_NewRef(text);
#if CYTHON_PEP393_ENABLED
return PyUnicode_FromKindAndData(PyUnicode_KIND(text),
PyUnicode_1BYTE_DATA(text) + start*PyUnicode_KIND(text), stop-start);
@@ -533,9 +671,8 @@
/////////////// unicode_tailmatch.proto ///////////////
-static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr,
- Py_ssize_t start, Py_ssize_t end, int direction); /*proto*/
-
+static int __Pyx_PyUnicode_Tailmatch(
+ PyObject* s, PyObject* substr, Py_ssize_t start, Py_ssize_t end, int direction); /*proto*/
/////////////// unicode_tailmatch ///////////////
@@ -543,26 +680,31 @@
// tuple of prefixes/suffixes, whereas it's much more common to
// test for a single unicode string.
-static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr,
- Py_ssize_t start, Py_ssize_t end, int direction) {
- if (unlikely(PyTuple_Check(substr))) {
- Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
- for (i = 0; i < count; i++) {
- Py_ssize_t result;
+static int __Pyx_PyUnicode_TailmatchTuple(PyObject* s, PyObject* substrings,
+ Py_ssize_t start, Py_ssize_t end, int direction) {
+ Py_ssize_t i, count = PyTuple_GET_SIZE(substrings);
+ for (i = 0; i < count; i++) {
+ Py_ssize_t result;
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
- result = PyUnicode_Tailmatch(s, PyTuple_GET_ITEM(substr, i),
- start, end, direction);
+ result = PyUnicode_Tailmatch(s, PyTuple_GET_ITEM(substrings, i),
+ start, end, direction);
#else
- PyObject* sub = PySequence_ITEM(substr, i);
- if (unlikely(!sub)) return -1;
- result = PyUnicode_Tailmatch(s, sub, start, end, direction);
- Py_DECREF(sub);
+ PyObject* sub = PySequence_ITEM(substrings, i);
+ if (unlikely(!sub)) return -1;
+ result = PyUnicode_Tailmatch(s, sub, start, end, direction);
+ Py_DECREF(sub);
#endif
- if (result) {
- return (int) result;
- }
+ if (result) {
+ return (int) result;
}
- return 0;
+ }
+ return 0;
+}
+
+static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr,
+ Py_ssize_t start, Py_ssize_t end, int direction) {
+ if (unlikely(PyTuple_Check(substr))) {
+ return __Pyx_PyUnicode_TailmatchTuple(s, substr, start, end, direction);
}
return (int) PyUnicode_Tailmatch(s, substr, start, end, direction);
}
@@ -633,26 +775,31 @@
return retval;
}
-static int __Pyx_PyBytes_Tailmatch(PyObject* self, PyObject* substr,
- Py_ssize_t start, Py_ssize_t end, int direction) {
- if (unlikely(PyTuple_Check(substr))) {
- Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
- for (i = 0; i < count; i++) {
- int result;
+static int __Pyx_PyBytes_TailmatchTuple(PyObject* self, PyObject* substrings,
+ Py_ssize_t start, Py_ssize_t end, int direction) {
+ Py_ssize_t i, count = PyTuple_GET_SIZE(substrings);
+ for (i = 0; i < count; i++) {
+ int result;
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
- result = __Pyx_PyBytes_SingleTailmatch(self, PyTuple_GET_ITEM(substr, i),
- start, end, direction);
+ result = __Pyx_PyBytes_SingleTailmatch(self, PyTuple_GET_ITEM(substrings, i),
+ start, end, direction);
#else
- PyObject* sub = PySequence_ITEM(substr, i);
- if (unlikely(!sub)) return -1;
- result = __Pyx_PyBytes_SingleTailmatch(self, sub, start, end, direction);
- Py_DECREF(sub);
+ PyObject* sub = PySequence_ITEM(substrings, i);
+ if (unlikely(!sub)) return -1;
+ result = __Pyx_PyBytes_SingleTailmatch(self, sub, start, end, direction);
+ Py_DECREF(sub);
#endif
- if (result) {
- return result;
- }
+ if (result) {
+ return result;
}
- return 0;
+ }
+ return 0;
+}
+
+static int __Pyx_PyBytes_Tailmatch(PyObject* self, PyObject* substr,
+ Py_ssize_t start, Py_ssize_t end, int direction) {
+ if (unlikely(PyTuple_Check(substr))) {
+ return __Pyx_PyBytes_TailmatchTuple(self, substr, start, end, direction);
}
return __Pyx_PyBytes_SingleTailmatch(self, substr, start, end, direction);
@@ -689,15 +836,15 @@
/////////////// bytes_index ///////////////
static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t index, int check_bounds) {
+ if (index < 0)
+ index += PyBytes_GET_SIZE(bytes);
if (check_bounds) {
Py_ssize_t size = PyBytes_GET_SIZE(bytes);
- if (unlikely(index >= size) | ((index < 0) & unlikely(index < -size))) {
+ if (unlikely(!__Pyx_is_valid_index(index, size))) {
PyErr_SetString(PyExc_IndexError, "string index out of range");
- return -1;
+ return (char) -1;
}
}
- if (index < 0)
- index += PyBytes_GET_SIZE(bytes);
return PyBytes_AS_STRING(bytes)[index];
}
@@ -742,25 +889,29 @@
//@substitute: naming
static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_count, Py_ssize_t result_ulength,
- CYTHON_UNUSED Py_UCS4 max_char) {
+ Py_UCS4 max_char) {
#if CYTHON_USE_UNICODE_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
PyObject *result_uval;
- int result_ukind;
+ int result_ukind, kind_shift;
Py_ssize_t i, char_pos;
void *result_udata;
+ CYTHON_MAYBE_UNUSED_VAR(max_char);
#if CYTHON_PEP393_ENABLED
// Py 3.3+ (post PEP-393)
result_uval = PyUnicode_New(result_ulength, max_char);
if (unlikely(!result_uval)) return NULL;
result_ukind = (max_char <= 255) ? PyUnicode_1BYTE_KIND : (max_char <= 65535) ? PyUnicode_2BYTE_KIND : PyUnicode_4BYTE_KIND;
+ kind_shift = (result_ukind == PyUnicode_4BYTE_KIND) ? 2 : result_ukind - 1;
result_udata = PyUnicode_DATA(result_uval);
#else
// Py 2.x/3.2 (pre PEP-393)
result_uval = PyUnicode_FromUnicode(NULL, result_ulength);
if (unlikely(!result_uval)) return NULL;
result_ukind = sizeof(Py_UNICODE);
+ kind_shift = (result_ukind == 4) ? 2 : result_ukind - 1;
result_udata = PyUnicode_AS_UNICODE(result_uval);
#endif
+ assert(kind_shift == 2 || kind_shift == 1 || kind_shift == 0);
char_pos = 0;
for (i=0; i < value_count; i++) {
@@ -773,14 +924,14 @@
ulength = __Pyx_PyUnicode_GET_LENGTH(uval);
if (unlikely(!ulength))
continue;
- if (unlikely(char_pos + ulength < 0))
+ if (unlikely((PY_SSIZE_T_MAX >> kind_shift) - ulength < char_pos))
goto overflow;
ukind = __Pyx_PyUnicode_KIND(uval);
udata = __Pyx_PyUnicode_DATA(uval);
if (!CYTHON_PEP393_ENABLED || ukind == result_ukind) {
- memcpy((char *)result_udata + char_pos * result_ukind, udata, ulength * result_ukind);
+ memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift));
} else {
- #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030300F0
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030300F0 || defined(_PyUnicode_FastCopyCharacters)
_PyUnicode_FastCopyCharacters(result_uval, char_pos, uval, 0, ulength);
#else
Py_ssize_t j;
@@ -800,8 +951,9 @@
return NULL;
#else
// non-CPython fallback
- result_ulength++;
- value_count++;
+ CYTHON_UNUSED_VAR(max_char);
+ CYTHON_UNUSED_VAR(result_ulength);
+ CYTHON_UNUSED_VAR(value_count);
return PyUnicode_Join($empty_unicode, value_tuple);
#endif
}
@@ -852,8 +1004,8 @@
#else
// non-CPython
{
- uval = NULL;
PyObject *sign = NULL, *padding = NULL;
+ uval = NULL;
if (uoffset > 0) {
prepend_sign = !!prepend_sign;
if (uoffset > prepend_sign) {
@@ -927,7 +1079,7 @@
{
// CPython calls PyNumber_Index() internally
ival = __Pyx_PyIndex_AsSsize_t(value);
- if (unlikely((ival < 0) | (ival > 255))) {
+ if (unlikely(!__Pyx_is_valid_index(ival, 256))) {
if (ival == -1 && PyErr_Occurred())
return -1;
goto bad_range;
@@ -949,7 +1101,7 @@
static CYTHON_INLINE int __Pyx_PyByteArray_Append(PyObject* bytearray, int value) {
PyObject *pyval, *retval;
#if CYTHON_COMPILING_IN_CPYTHON
- if (likely((value >= 0) & (value <= 255))) {
+ if (likely(__Pyx_is_valid_index(value, 256))) {
Py_ssize_t n = Py_SIZE(bytearray);
if (likely(n != PY_SSIZE_T_MAX)) {
if (unlikely(PyByteArray_Resize(bytearray, n + 1) < 0))
@@ -1037,11 +1189,12 @@
likely(PyString_CheckExact(s)) ? PyUnicode_FromEncodedObject(s, NULL, "strict") : \
PyObject_Format(s, f))
#elif CYTHON_USE_TYPE_SLOTS
- // Py3 nicely returns unicode strings from str() which makes this quite efficient for builtin types
+ // Py3 nicely returns unicode strings from str() and repr(), which makes this quite efficient for builtin types.
+ // In Py3.8+, tp_str() delegates to tp_repr(), so we call tp_repr() directly here.
#define __Pyx_PyObject_FormatSimple(s, f) ( \
likely(PyUnicode_CheckExact(s)) ? (Py_INCREF(s), s) : \
- likely(PyLong_CheckExact(s)) ? PyLong_Type.tp_str(s) : \
- likely(PyFloat_CheckExact(s)) ? PyFloat_Type.tp_str(s) : \
+ likely(PyLong_CheckExact(s)) ? PyLong_Type.tp_repr(s) : \
+ likely(PyFloat_CheckExact(s)) ? PyFloat_Type.tp_repr(s) : \
PyObject_Format(s, f))
#else
#define __Pyx_PyObject_FormatSimple(s, f) ( \
@@ -1076,3 +1229,46 @@
Py_DECREF(s);
return result;
}
+
+
+//////////////////// PyUnicode_Unicode.proto ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Unicode(PyObject *obj);/*proto*/
+
+//////////////////// PyUnicode_Unicode ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Unicode(PyObject *obj) {
+ if (unlikely(obj == Py_None))
+ obj = PYUNICODE("None");
+ return __Pyx_NewRef(obj);
+}
+
+
+//////////////////// PyObject_Unicode.proto ////////////////////
+
+#if PY_MAJOR_VERSION >= 3
+#define __Pyx_PyObject_Unicode(obj) \
+ (likely(PyUnicode_CheckExact(obj)) ? __Pyx_NewRef(obj) : PyObject_Str(obj))
+#else
+#define __Pyx_PyObject_Unicode(obj) \
+ (likely(PyUnicode_CheckExact(obj)) ? __Pyx_NewRef(obj) : PyObject_Unicode(obj))
+#endif
+
+
+//////////////////// PyStr_Str.proto ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyStr_Str(PyObject *obj);/*proto*/
+
+//////////////////// PyStr_Str ////////////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PyStr_Str(PyObject *obj) {
+ if (unlikely(obj == Py_None))
+ obj = PYIDENT("None");
+ return __Pyx_NewRef(obj);
+}
+
+
+//////////////////// PyObject_Str.proto ////////////////////
+
+#define __Pyx_PyObject_Str(obj) \
+ (likely(PyString_CheckExact(obj)) ? __Pyx_NewRef(obj) : PyObject_Str(obj))
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/TestCythonScope.pyx cython-0.20.1+1~202203241016-9537/Cython/Utility/TestCythonScope.pyx
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/TestCythonScope.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/TestCythonScope.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -1,6 +1,11 @@
########## TestClass ##########
# These utilities are for testing purposes
+# The "cythonscope" test calls METH_O functions with their (self, arg) signature.
+# cython: always_allow_keywords=False
+
+from __future__ import print_function
+
cdef extern from *:
cdef object __pyx_test_dep(object)
@@ -12,32 +17,32 @@
self.value = value
def __str__(self):
- return 'TestClass(%d)' % self.value
+ return f'TestClass({self.value})'
cdef cdef_method(self, int value):
- print 'Hello from cdef_method', value
+ print('Hello from cdef_method', value)
cpdef cpdef_method(self, int value):
- print 'Hello from cpdef_method', value
+ print('Hello from cpdef_method', value)
def def_method(self, int value):
- print 'Hello from def_method', value
+ print('Hello from def_method', value)
@cname('cdef_cname')
cdef cdef_cname_method(self, int value):
- print "Hello from cdef_cname_method", value
+ print("Hello from cdef_cname_method", value)
@cname('cpdef_cname')
cpdef cpdef_cname_method(self, int value):
- print "Hello from cpdef_cname_method", value
+ print("Hello from cpdef_cname_method", value)
@cname('def_cname')
def def_cname_method(self, int value):
- print "Hello from def_cname_method", value
+ print("Hello from def_cname_method", value)
@cname('__pyx_test_call_other_cy_util')
cdef test_call(obj):
- print 'test_call'
+ print('test_call')
__pyx_test_dep(obj)
@cname('__pyx_TestClass_New')
@@ -46,19 +51,20 @@
########### TestDep ##########
+from __future__ import print_function
+
@cname('__pyx_test_dep')
cdef test_dep(obj):
- print 'test_dep', obj
+ print('test_dep', obj)
########## TestScope ##########
@cname('__pyx_testscope')
cdef object _testscope(int value):
- return "hello from cython scope, value=%d" % value
+ return f"hello from cython scope, value={value}"
########## View.TestScope ##########
@cname('__pyx_view_testscope')
cdef object _testscope(int value):
- return "hello from cython.view scope, value=%d" % value
-
+ return f"hello from cython.view scope, value={value}"
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utility/TypeConversion.c cython-0.20.1+1~202203241016-9537/Cython/Utility/TypeConversion.c
--- cython-0.20.1+1~201611251650-6686/Cython/Utility/TypeConversion.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utility/TypeConversion.c 2022-03-24 10:16:46.000000000 +0000
@@ -16,6 +16,14 @@
(is_signed || likely(v < (type)PY_SSIZE_T_MAX || \
v == (type)PY_SSIZE_T_MAX))) )
+static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) {
+ // Optimisation from Section 14.2 "Bounds Checking" in
+ // https://www.agner.org/optimize/optimizing_cpp.pdf
+ // See https://bugs.python.org/issue28397
+ // The cast to unsigned effectively tests for "0 <= i < limit".
+ return (size_t) i < (size_t) limit;
+}
+
// fast and unsafe abs(Py_ssize_t) that ignores the overflow for (-PY_SSIZE_T_MAX-1)
#if defined (__cplusplus) && __cplusplus >= 201103L
#include
@@ -24,10 +32,10 @@
#define __Pyx_sst_abs(value) abs(value)
#elif SIZEOF_LONG >= SIZEOF_SIZE_T
#define __Pyx_sst_abs(value) labs(value)
-#elif defined (_MSC_VER) && defined (_M_X64)
+#elif defined (_MSC_VER)
// abs() is defined for long, but 64-bits type on MSVC is long long.
// Use MS-specific _abs64 instead.
- #define __Pyx_sst_abs(value) _abs64(value)
+ #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value))
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define __Pyx_sst_abs(value) llabs(value)
#elif defined (__GNUC__)
@@ -37,8 +45,8 @@
#define __Pyx_sst_abs(value) ((value<0) ? -value : value)
#endif
-static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*);
-static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
#define __Pyx_PyByteArray_FromString(s) PyByteArray_FromStringAndSize((const char*)s, strlen((const char*)s))
#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l)
@@ -54,23 +62,38 @@
#define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize
#endif
-#define __Pyx_PyObject_AsSString(s) ((signed char*) __Pyx_PyObject_AsString(s))
-#define __Pyx_PyObject_AsUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s))
+#define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
#define __Pyx_PyStr_FromCString(s) __Pyx_PyStr_FromString((const char*)s)
#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s)
-#if PY_MAJOR_VERSION < 3
+// There used to be a Py_UNICODE_strlen() in CPython 3.x, but it is deprecated since Py3.3.
+#if CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const wchar_t *u)
+{
+ const wchar_t *u_end = u;
+ while (*u_end++) ;
+ return (size_t)(u_end - u - 1);
+}
+#else
static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u)
{
const Py_UNICODE *u_end = u;
while (*u_end++) ;
return (size_t)(u_end - u - 1);
}
-#else
-#define __Pyx_Py_UNICODE_strlen Py_UNICODE_strlen
#endif
#define __Pyx_PyUnicode_FromUnicode(u) PyUnicode_FromUnicode(u, __Pyx_Py_UNICODE_strlen(u))
@@ -79,12 +102,17 @@
#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
-#define __Pyx_PyBool_FromLong(b) ((b) ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False))
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*);
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
+#define __Pyx_PySequence_Tuple(obj) \
+ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj))
+
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t);
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
#if CYTHON_ASSUME_SAFE_MACROS
#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
@@ -98,7 +126,8 @@
#else
#define __Pyx_PyNumber_Int(x) (PyInt_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Int(x))
#endif
-#define __Pyx_PyNumber_Float(x) (PyFloat_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Float(x))
+// __Pyx_PyNumber_Float is now in it's own section since it has dependencies (needed to make
+// string conversion work the same in all circumstances)
#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
static int __Pyx_sys_getdefaultencoding_not_ascii;
@@ -169,7 +198,7 @@
if (!default_encoding) goto bad;
default_encoding_c = PyBytes_AsString(default_encoding);
if (!default_encoding_c) goto bad;
- __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c));
+ __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c) + 1);
if (!__PYX_DEFAULT_STRING_ENCODING) goto bad;
strcpy(__PYX_DEFAULT_STRING_ENCODING, default_encoding_c);
Py_DECREF(default_encoding);
@@ -189,59 +218,71 @@
return __Pyx_PyUnicode_FromStringAndSize(c_str, (Py_ssize_t)strlen(c_str));
}
-static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) {
+// Py3.7 returns a "const char*" for unicode strings
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) {
Py_ssize_t ignore;
return __Pyx_PyObject_AsStringAndSize(o, &ignore);
}
-static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
-#if CYTHON_COMPILING_IN_CPYTHON && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)
- if (
-#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
- __Pyx_sys_getdefaultencoding_not_ascii &&
-#endif
- PyUnicode_Check(o)) {
-#if PY_VERSION_HEX < 0x03030000
- char* defenc_c;
- // borrowed reference, cached internally in 'o' by CPython
- PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL);
- if (!defenc) return NULL;
- defenc_c = PyBytes_AS_STRING(defenc);
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT
+#if !CYTHON_PEP393_ENABLED
+static const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+ char* defenc_c;
+ // borrowed reference, cached internally in 'o' by CPython
+ PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL);
+ if (!defenc) return NULL;
+ defenc_c = PyBytes_AS_STRING(defenc);
#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
- {
- char* end = defenc_c + PyBytes_GET_SIZE(defenc);
- char* c;
- for (c = defenc_c; c < end; c++) {
- if ((unsigned char) (*c) >= 128) {
- // raise the error
- PyUnicode_AsASCIIString(o);
- return NULL;
- }
+ {
+ char* end = defenc_c + PyBytes_GET_SIZE(defenc);
+ char* c;
+ for (c = defenc_c; c < end; c++) {
+ if ((unsigned char) (*c) >= 128) {
+ // raise the error
+ PyUnicode_AsASCIIString(o);
+ return NULL;
}
}
+ }
#endif /*__PYX_DEFAULT_STRING_ENCODING_IS_ASCII*/
- *length = PyBytes_GET_SIZE(defenc);
- return defenc_c;
-#else /* PY_VERSION_HEX < 0x03030000 */
- if (__Pyx_PyUnicode_READY(o) == -1) return NULL;
+ *length = PyBytes_GET_SIZE(defenc);
+ return defenc_c;
+}
+
+#else /* CYTHON_PEP393_ENABLED: */
+
+static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+ if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL;
#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
- if (PyUnicode_IS_ASCII(o)) {
- // cached for the lifetime of the object
- *length = PyUnicode_GET_LENGTH(o);
- return PyUnicode_AsUTF8(o);
- } else {
- // raise the error
- PyUnicode_AsASCIIString(o);
- return NULL;
- }
+ if (likely(PyUnicode_IS_ASCII(o))) {
+ // cached for the lifetime of the object
+ *length = PyUnicode_GET_LENGTH(o);
+ return PyUnicode_AsUTF8(o);
+ } else {
+ // raise the error
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
#else /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII */
- return PyUnicode_AsUTF8AndSize(o, length);
+ return PyUnicode_AsUTF8AndSize(o, length);
#endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII */
-#endif /* PY_VERSION_HEX < 0x03030000 */
+}
+#endif /* CYTHON_PEP393_ENABLED */
+#endif
+
+// Py3.7 returns a "const char*" for unicode strings
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT
+ if (
+#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ __Pyx_sys_getdefaultencoding_not_ascii &&
+#endif
+ PyUnicode_Check(o)) {
+ return __Pyx_PyUnicode_AsStringAndSize(o, length);
} else
#endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT */
-#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE))
+#if (!CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_LIMITED_API) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE))
if (PyByteArray_Check(o)) {
*length = PyByteArray_GET_SIZE(o);
return PyByteArray_AS_STRING(o);
@@ -265,6 +306,40 @@
else return PyObject_IsTrue(x);
}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) {
+ int retval;
+ if (unlikely(!x)) return -1;
+ retval = __Pyx_PyObject_IsTrue(x);
+ Py_DECREF(x);
+ return retval;
+}
+
+static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) {
+ __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(Py_TYPE(result));
+#if PY_MAJOR_VERSION >= 3
+ if (PyLong_Check(result)) {
+ // CPython issue #17576: warn if 'result' not of exact type int.
+ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
+ "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). "
+ "The ability to return an instance of a strict subclass of int is deprecated, "
+ "and may be removed in a future version of Python.",
+ result_type_name)) {
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+ }
+ __Pyx_DECREF_TypeName(result_type_name);
+ return result;
+ }
+#endif
+ PyErr_Format(PyExc_TypeError,
+ "__%.4s__ returned non-%.4s (type " __Pyx_FMT_TYPENAME ")",
+ type_name, type_name, result_type_name);
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+}
+
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) {
#if CYTHON_USE_TYPE_SLOTS
PyNumberMethods *m;
@@ -272,9 +347,9 @@
const char *name = NULL;
PyObject *res = NULL;
#if PY_MAJOR_VERSION < 3
- if (PyInt_Check(x) || PyLong_Check(x))
+ if (likely(PyInt_Check(x) || PyLong_Check(x)))
#else
- if (PyLong_Check(x))
+ if (likely(PyLong_Check(x)))
#endif
return __Pyx_NewRef(x);
#if CYTHON_USE_TYPE_SLOTS
@@ -282,32 +357,30 @@
#if PY_MAJOR_VERSION < 3
if (m && m->nb_int) {
name = "int";
- res = PyNumber_Int(x);
+ res = m->nb_int(x);
}
else if (m && m->nb_long) {
name = "long";
- res = PyNumber_Long(x);
+ res = m->nb_long(x);
}
#else
- if (m && m->nb_int) {
+ if (likely(m && m->nb_int)) {
name = "int";
- res = PyNumber_Long(x);
+ res = m->nb_int(x);
}
#endif
#else
- res = PyNumber_Int(x);
+ if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) {
+ res = PyNumber_Int(x);
+ }
#endif
- if (res) {
+ if (likely(res)) {
#if PY_MAJOR_VERSION < 3
- if (!PyInt_Check(res) && !PyLong_Check(res)) {
+ if (unlikely(!PyInt_Check(res) && !PyLong_Check(res))) {
#else
- if (!PyLong_Check(res)) {
+ if (unlikely(!PyLong_CheckExact(res))) {
#endif
- PyErr_Format(PyExc_TypeError,
- "__%.4s__ returned non-%.4s (type %.200s)",
- name, name, Py_TYPE(res)->tp_name);
- Py_DECREF(res);
- return NULL;
+ return __Pyx_PyNumber_IntOrLongWrongResultType(res, name);
}
}
else if (!PyErr_Occurred()) {
@@ -327,7 +400,7 @@
if (sizeof(Py_ssize_t) >= sizeof(long))
return PyInt_AS_LONG(b);
else
- return PyInt_AsSsize_t(x);
+ return PyInt_AsSsize_t(b);
}
#endif
if (likely(PyLong_CheckExact(b))) {
@@ -362,10 +435,92 @@
return ival;
}
+
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) {
+ if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) {
+ return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o);
+#if PY_MAJOR_VERSION < 3
+ } else if (likely(PyInt_CheckExact(o))) {
+ return PyInt_AS_LONG(o);
+#endif
+ } else {
+ Py_ssize_t ival;
+ PyObject *x;
+ x = PyNumber_Index(o);
+ if (!x) return -1;
+ ival = PyInt_AsLong(x);
+ Py_DECREF(x);
+ return ival;
+ }
+}
+
+
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
+ return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
+}
+
+
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
return PyInt_FromSize_t(ival);
}
+/////////////// pynumber_float.proto ///////////////
+
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj); /* proto */
+#define __Pyx_PyNumber_Float(x) (PyFloat_CheckExact(x) ? __Pyx_NewRef(x) : __Pyx__PyNumber_Float(x))
+
+/////////////// pynumber_float ///////////////
+//@requires: Optimize.c::pybytes_as_double
+//@requires: Optimize.c::pyunicode_as_double
+
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj) {
+ // 'obj is PyFloat' is handled in the calling macro
+ double val;
+ if (PyLong_CheckExact(obj)) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)obj)->ob_digit;
+ switch (Py_SIZE(obj)) {
+ case 0:
+ val = 0.0;
+ goto no_error;
+ // single digit PyLong values always cast safely to double
+ case 1:
+ val = (double) digits[0];
+ goto no_error;
+ case -1:
+ val = (double) - (sdigit) digits[0];
+ goto no_error;
+ default:
+ val = PyLong_AsDouble(obj);
+ }
+#else
+ val = PyLong_AsDouble(obj);
+#endif
+ } else if (PyUnicode_CheckExact(obj)) {
+ val = __Pyx_PyUnicode_AsDouble(obj);
+ } else if (PyBytes_CheckExact(obj)) {
+ val = __Pyx_PyBytes_AsDouble(obj);
+ } else if (PyByteArray_CheckExact(obj)) {
+ val = __Pyx_PyByteArray_AsDouble(obj);
+ } else {
+ return PyNumber_Float(obj);
+ }
+
+ if (unlikely(val == -1 && PyErr_Occurred())) {
+ return NULL;
+ }
+no_error:
+ return PyFloat_FromDouble(val);
+}
+
+/////////////// GCCDiagnostics.proto ///////////////
+
+// GCC diagnostic pragmas were introduced in GCC 4.6
+// Used to silence conversion warnings that are ok but cannot be avoided.
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define __Pyx_HAS_GCC_DIAGNOSTIC
+#endif
+
/////////////// ToPyCTupleUtility.proto ///////////////
static PyObject* {{funcname}}({{struct_type_decl}});
@@ -399,7 +554,10 @@
{{struct_type_decl}} result;
if (!PyTuple_Check(o) || PyTuple_GET_SIZE(o) != {{size}}) {
- PyErr_Format(PyExc_TypeError, "Expected %.16s of size %d, got %.200s", "a tuple", {{size}}, Py_TYPE(o)->tp_name);
+ __Pyx_TypeName o_type_name = __Pyx_PyType_GetName(Py_TYPE(o));
+ PyErr_Format(PyExc_TypeError,
+ "Expected a tuple of size %d, got " __Pyx_FMT_TYPENAME, {{size}}, o_type_name);
+ __Pyx_DECREF_TypeName(o_type_name);
goto bad;
}
@@ -474,18 +632,23 @@
/////////////// ObjectAsUCS4 ///////////////
-static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject* x) {
- long ival;
- ival = __Pyx_PyInt_As_long(x);
- if (unlikely(ival < 0)) {
+static Py_UCS4 __Pyx__PyObject_AsPy_UCS4_raise_error(long ival) {
+ if (ival < 0) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_OverflowError,
"cannot convert negative value to Py_UCS4");
- return (Py_UCS4)-1;
- } else if (unlikely(ival > 1114111)) {
+ } else {
PyErr_SetString(PyExc_OverflowError,
"value too large to convert to Py_UCS4");
- return (Py_UCS4)-1;
+ }
+ return (Py_UCS4)-1;
+}
+
+static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject* x) {
+ long ival;
+ ival = __Pyx_PyInt_As_long(x);
+ if (unlikely(!__Pyx_is_valid_index(ival, 1114111 + 1))) {
+ return __Pyx__PyObject_AsPy_UCS4_raise_error(ival);
}
return (Py_UCS4)ival;
}
@@ -527,14 +690,16 @@
#endif
ival = __Pyx_PyInt_As_long(x);
}
- if (unlikely(ival < 0)) {
- if (!PyErr_Occurred())
+ if (unlikely(!__Pyx_is_valid_index(ival, maxval + 1))) {
+ if (ival < 0) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_OverflowError,
+ "cannot convert negative value to Py_UNICODE");
+ return (Py_UNICODE)-1;
+ } else {
PyErr_SetString(PyExc_OverflowError,
- "cannot convert negative value to Py_UNICODE");
- return (Py_UNICODE)-1;
- } else if (unlikely(ival > maxval)) {
- PyErr_SetString(PyExc_OverflowError,
- "value too large to convert to Py_UNICODE");
+ "value too large to convert to Py_UNICODE");
+ }
return (Py_UNICODE)-1;
}
return (Py_UNICODE)ival;
@@ -546,9 +711,17 @@
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value);
/////////////// CIntToPy ///////////////
+//@requires: GCCDiagnostics
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
const int is_unsigned = neg_one > const_zero;
if (is_unsigned) {
if (sizeof({{TYPE}}) < sizeof(long)) {
@@ -605,7 +778,8 @@
};
static const char DIGITS_HEX[2*16+1] = {
- "0123456789abcdef0123456789ABCDEF"
+ "0123456789abcdef"
+ "0123456789ABCDEF"
};
@@ -614,10 +788,10 @@
static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t width, char padding_char, char format_char);
/////////////// CIntToPyUnicode ///////////////
+//@requires: StringTools.c::IncludeStringH
//@requires: StringTools.c::BuildPyUnicode
//@requires: CIntToDigits
-
-#include
+//@requires: GCCDiagnostics
// NOTE: inlining because most arguments are constant, which collapses lots of code below
@@ -627,53 +801,59 @@
// 'dpos' points to end of digits array + 1 initially to allow for pre-decrement looping
char *dpos, *end = digits + sizeof({{TYPE}})*3+2;
const char *hex_digits = DIGITS_HEX;
- Py_ssize_t ulength;
- int length, prepend_sign, last_one_off;
+ Py_ssize_t length, ulength;
+ int prepend_sign, last_one_off;
{{TYPE}} remaining;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
const int is_unsigned = neg_one > const_zero;
if (format_char == 'X') {
hex_digits += 16;
format_char = 'x';
- };
+ }
// surprise: even trivial sprintf() calls don't get optimised in gcc (4.8)
remaining = value; /* not using abs(value) to avoid overflow problems */
last_one_off = 0;
dpos = end;
- while (remaining != 0) {
+ do {
int digit_pos;
switch (format_char) {
case 'o':
- digit_pos = abs(remaining % (8*8));
- remaining = remaining / (8*8);
+ digit_pos = abs((int)(remaining % (8*8)));
+ remaining = ({{TYPE}}) (remaining / (8*8));
dpos -= 2;
- *(uint16_t*)dpos = ((uint16_t*)DIGIT_PAIRS_8)[digit_pos]; /* copy 2 digits at a time */
+ memcpy(dpos, DIGIT_PAIRS_8 + digit_pos * 2, 2); /* copy 2 digits at a time, unaligned */
last_one_off = (digit_pos < 8);
break;
case 'd':
- digit_pos = abs(remaining % (10*10));
- remaining = remaining / (10*10);
+ digit_pos = abs((int)(remaining % (10*10)));
+ remaining = ({{TYPE}}) (remaining / (10*10));
dpos -= 2;
- *(uint16_t*)dpos = ((uint16_t*)DIGIT_PAIRS_10)[digit_pos]; /* copy 2 digits at a time */
+ memcpy(dpos, DIGIT_PAIRS_10 + digit_pos * 2, 2); /* copy 2 digits at a time, unaligned */
last_one_off = (digit_pos < 10);
break;
case 'x':
- *(--dpos) = hex_digits[abs(remaining % 16)];
- remaining = remaining / 16;
+ *(--dpos) = hex_digits[abs((int)(remaining % 16))];
+ remaining = ({{TYPE}}) (remaining / 16);
break;
default:
assert(0);
break;
}
- }
- if (last_one_off) {
- assert(*dpos == '0');
- dpos++;
- } else if (unlikely(dpos == end)) {
- *(--dpos) = '0';
- }
+ } while (unlikely(remaining != 0));
+
+ // Correct dpos by 1 if we read an excess digit.
+ assert(!last_one_off || *dpos == '0');
+ dpos += last_one_off;
+
length = end - dpos;
ulength = length;
prepend_sign = 0;
@@ -693,7 +873,7 @@
if (ulength == 1) {
return PyUnicode_FromOrdinal(*dpos);
}
- return __Pyx_PyUnicode_BuildFromAscii(ulength, dpos, length, prepend_sign, padding_char);
+ return __Pyx_PyUnicode_BuildFromAscii(ulength, dpos, (int) length, prepend_sign, padding_char);
}
@@ -756,15 +936,23 @@
/////////////// CIntFromPy ///////////////
//@requires: CIntFromPyVerify
+//@requires: GCCDiagnostics
{{py: from Cython.Utility import pylong_join }}
static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
const int is_unsigned = neg_one > const_zero;
#if PY_MAJOR_VERSION < 3
if (likely(PyInt_Check(x))) {
- if (sizeof({{TYPE}}) < sizeof(long)) {
+ if ((sizeof({{TYPE}}) < sizeof(long))) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, long, PyInt_AS_LONG(x))
} else {
long val = PyInt_AS_LONG(x);
@@ -784,10 +972,10 @@
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, digits[0])
{{for _size in (2, 3, 4)}}
case {{_size}}:
- if (8 * sizeof({{TYPE}}) > {{_size-1}} * PyLong_SHIFT) {
- if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT) {
+ if ((8 * sizeof({{TYPE}}) > {{_size-1}} * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, {{pylong_join(_size, 'digits')}})
- } else if (8 * sizeof({{TYPE}}) >= {{_size}} * PyLong_SHIFT) {
+ } else if ((8 * sizeof({{TYPE}}) >= {{_size}} * PyLong_SHIFT)) {
return ({{TYPE}}) {{pylong_join(_size, 'digits', TYPE)}};
}
}
@@ -809,10 +997,10 @@
goto raise_neg_overflow;
}
#endif
- if (sizeof({{TYPE}}) <= sizeof(unsigned long)) {
+ if ((sizeof({{TYPE}}) <= sizeof(unsigned long))) {
__PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, unsigned long, PyLong_AsUnsignedLong(x))
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) {
+ } else if ((sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG))) {
__PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
#endif
}
@@ -827,10 +1015,10 @@
{{for _size in (2, 3, 4)}}
{{for _case in (-_size, _size)}}
case {{_case}}:
- if (8 * sizeof({{TYPE}}){{' - 1' if _case < 0 else ''}} > {{_size-1}} * PyLong_SHIFT) {
- if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT) {
+ if ((8 * sizeof({{TYPE}}){{' - 1' if _case < 0 else ''}} > {{_size-1}} * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, {{'long' if _case < 0 else 'unsigned long'}}, {{'-(long) ' if _case < 0 else ''}}{{pylong_join(_size, 'digits')}})
- } else if (8 * sizeof({{TYPE}}) - 1 > {{_size}} * PyLong_SHIFT) {
+ } else if ((8 * sizeof({{TYPE}}) - 1 > {{_size}} * PyLong_SHIFT)) {
return ({{TYPE}}) ({{'((%s)-1)*' % TYPE if _case < 0 else ''}}{{pylong_join(_size, 'digits', TYPE)}});
}
}
@@ -839,18 +1027,18 @@
{{endfor}}
}
#endif
- if (sizeof({{TYPE}}) <= sizeof(long)) {
+ if ((sizeof({{TYPE}}) <= sizeof(long))) {
__PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, long, PyLong_AsLong(x))
#ifdef HAVE_LONG_LONG
- } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) {
+ } else if ((sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG))) {
__PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, PY_LONG_LONG, PyLong_AsLongLong(x))
#endif
}
}
{
-#if CYTHON_COMPILING_IN_PYPY && !defined(_PyLong_AsByteArray)
+#if (CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) && !defined(_PyLong_AsByteArray)
PyErr_SetString(PyExc_RuntimeError,
- "_PyLong_AsByteArray() not available in PyPy, cannot convert large numbers");
+ "_PyLong_AsByteArray() not available, cannot convert large numbers");
#else
{{TYPE}} val;
PyObject *v = __Pyx_PyNumber_IntOrLong(x);
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utils.pxd cython-0.20.1+1~202203241016-9537/Cython/Utils.pxd
--- cython-0.20.1+1~201611251650-6686/Cython/Utils.pxd 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utils.pxd 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,3 @@
+
+cdef class _TryFinallyGeneratorContextManager:
+ cdef object _gen
diff -Nru cython-0.20.1+1~201611251650-6686/Cython/Utils.py cython-0.20.1+1~202203241016-9537/Cython/Utils.py
--- cython-0.20.1+1~201611251650-6686/Cython/Utils.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Cython/Utils.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,39 +1,124 @@
-#
-# Cython -- Things that don't belong
-# anywhere else in particular
-#
+"""
+Cython -- Things that don't belong anywhere else in particular
+"""
from __future__ import absolute_import
+import cython
+
+cython.declare(
+ basestring=object,
+ os=object, sys=object, re=object, io=object, codecs=object, glob=object, shutil=object, tempfile=object,
+ cython_version=object,
+ _function_caches=list, _parse_file_version=object, _match_file_encoding=object,
+)
+
try:
from __builtin__ import basestring
except ImportError:
basestring = str
+try:
+ FileNotFoundError
+except NameError:
+ FileNotFoundError = OSError
+
import os
import sys
import re
import io
import codecs
+import glob
import shutil
-from contextlib import contextmanager
+import tempfile
+from functools import wraps
+
+from . import __version__ as cython_version
+
+PACKAGE_FILES = ("__init__.py", "__init__.pyc", "__init__.pyx", "__init__.pxd")
+
+_build_cache_name = "__{0}_cache".format
+_CACHE_NAME_PATTERN = re.compile(r"^__(.+)_cache$")
modification_time = os.path.getmtime
+GENERATED_BY_MARKER = "/* Generated by Cython %s */" % cython_version
+GENERATED_BY_MARKER_BYTES = GENERATED_BY_MARKER.encode('us-ascii')
+
+
+class _TryFinallyGeneratorContextManager(object):
+ """
+ Fast, bare minimum @contextmanager, only for try-finally, not for exception handling.
+ """
+ def __init__(self, gen):
+ self._gen = gen
+
+ def __enter__(self):
+ return next(self._gen)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ try:
+ next(self._gen)
+ except (StopIteration, GeneratorExit):
+ pass
+
+
+def try_finally_contextmanager(gen_func):
+ @wraps(gen_func)
+ def make_gen(*args, **kwargs):
+ return _TryFinallyGeneratorContextManager(gen_func(*args, **kwargs))
+ return make_gen
+
+
+_function_caches = []
+
+
+def clear_function_caches():
+ for cache in _function_caches:
+ cache.clear()
+
def cached_function(f):
cache = {}
+ _function_caches.append(cache)
uncomputed = object()
+
+ @wraps(f)
def wrapper(*args):
res = cache.get(args, uncomputed)
if res is uncomputed:
res = cache[args] = f(*args)
return res
+
wrapper.uncached = f
return wrapper
+
+def _find_cache_attributes(obj):
+ """The function iterates over the attributes of the object and,
+ if it finds the name of the cache, it returns it and the corresponding method name.
+ The method may not be present in the object.
+ """
+ for attr_name in dir(obj):
+ match = _CACHE_NAME_PATTERN.match(attr_name)
+ if match is not None:
+ yield attr_name, match.group(1)
+
+
+def clear_method_caches(obj):
+ """Removes every cache found in the object,
+ if a corresponding method exists for that cache.
+ """
+ for cache_name, method_name in _find_cache_attributes(obj):
+ if hasattr(obj, method_name):
+ delattr(obj, cache_name)
+ # if there is no corresponding method, then we assume
+ # that this attribute was not created by our cached method
+
+
def cached_method(f):
- cache_name = '__%s_cache' % f.__name__
+ cache_name = _build_cache_name(f.__name__)
+
def wrapper(self, *args):
cache = getattr(self, cache_name, None)
if cache is None:
@@ -43,8 +128,10 @@
return cache[args]
res = cache[args] = f(self, *args)
return res
+
return wrapper
+
def replace_suffix(path, newsuf):
base, _ = os.path.splitext(path)
return base + newsuf
@@ -70,6 +157,9 @@
# failed compilation.
# Also sets access and modification times back to
# those specified by st (a stat struct).
+ if not is_cython_generated_file(path, allow_failed=True, if_not_found=False):
+ return
+
try:
f = open_new_file(path)
except EnvironmentError:
@@ -81,6 +171,42 @@
if st:
os.utime(path, (st.st_atime, st.st_mtime-1))
+
+def is_cython_generated_file(path, allow_failed=False, if_not_found=True):
+ failure_marker = b"#error Do not use this file, it is the result of a failed Cython compilation."
+ file_content = None
+ if os.path.exists(path):
+ try:
+ with open(path, "rb") as f:
+ file_content = f.read(len(failure_marker))
+ except (OSError, IOError):
+ pass # Probably just doesn't exist any more
+
+ if file_content is None:
+ # file does not exist (yet)
+ return if_not_found
+
+ return (
+ # Cython C file?
+ file_content.startswith(b"/* Generated by Cython ") or
+ # Cython output file after previous failures?
+ (allow_failed and file_content == failure_marker) or
+ # Let's allow overwriting empty files as well. They might have resulted from previous failures.
+ not file_content
+ )
+
+
+def file_generated_by_this_cython(path):
+ file_content = b''
+ if os.path.exists(path):
+ try:
+ with open(path, "rb") as f:
+ file_content = f.read(len(GENERATED_BY_MARKER_BYTES))
+ except (OSError, IOError):
+ pass # Probably just doesn't exist any more
+ return file_content and file_content.startswith(GENERATED_BY_MARKER_BYTES)
+
+
def file_newer_than(path, time):
ftime = modification_time(path)
return ftime > time
@@ -114,54 +240,6 @@
@cached_function
-def search_include_directories(dirs, qualified_name, suffix, pos,
- include=False, sys_path=False):
- # Search the list of include directories for the given
- # file name. If a source file position is given, first
- # searches the directory containing that file. Returns
- # None if not found, but does not report an error.
- # The 'include' option will disable package dereferencing.
- # If 'sys_path' is True, also search sys.path.
- if sys_path:
- dirs = dirs + tuple(sys.path)
- if pos:
- file_desc = pos[0]
- from Cython.Compiler.Scanning import FileSourceDescriptor
- if not isinstance(file_desc, FileSourceDescriptor):
- raise RuntimeError("Only file sources for code supported")
- if include:
- dirs = (os.path.dirname(file_desc.filename),) + dirs
- else:
- dirs = (find_root_package_dir(file_desc.filename),) + dirs
-
- dotted_filename = qualified_name
- if suffix:
- dotted_filename += suffix
- if not include:
- names = qualified_name.split('.')
- package_names = tuple(names[:-1])
- module_name = names[-1]
- module_filename = module_name + suffix
- package_filename = "__init__" + suffix
-
- for dir in dirs:
- path = os.path.join(dir, dotted_filename)
- if path_exists(path):
- return path
- if not include:
- package_dir = check_package_dir(dir, package_names)
- if package_dir is not None:
- path = os.path.join(package_dir, module_filename)
- if path_exists(path):
- return path
- path = os.path.join(dir, package_dir, module_name,
- package_filename)
- if path_exists(path):
- return path
- return None
-
-
-@cached_function
def find_root_package_dir(file_path):
dir = os.path.dirname(file_path)
if file_path == dir:
@@ -171,24 +249,31 @@
else:
return dir
+
@cached_function
-def check_package_dir(dir, package_names):
+def check_package_dir(dir_path, package_names):
+ namespace = True
for dirname in package_names:
- dir = os.path.join(dir, dirname)
- if not is_package_dir(dir):
- return None
- return dir
+ dir_path = os.path.join(dir_path, dirname)
+ has_init = contains_init(dir_path)
+ if has_init:
+ namespace = False
+ return dir_path, namespace
+
@cached_function
-def is_package_dir(dir_path):
- for filename in ("__init__.py",
- "__init__.pyc",
- "__init__.pyx",
- "__init__.pxd"):
+def contains_init(dir_path):
+ for filename in PACKAGE_FILES:
path = os.path.join(dir_path, filename)
if path_exists(path):
return 1
+
+def is_package_dir(dir_path):
+ if contains_init(dir_path):
+ return 1
+
+
@cached_function
def path_exists(path):
# try on the filesystem first
@@ -213,6 +298,40 @@
pass
return False
+
+_parse_file_version = re.compile(r".*[.]cython-([0-9]+)[.][^./\\]+$").findall
+
+
+@cached_function
+def find_versioned_file(directory, filename, suffix,
+ _current_version=int(re.sub(r"^([0-9]+)[.]([0-9]+).*", r"\1\2", cython_version))):
+ """
+ Search a directory for versioned pxd files, e.g. "lib.cython-30.pxd" for a Cython 3.0+ version.
+
+ @param directory: the directory to search
+ @param filename: the filename without suffix
+ @param suffix: the filename extension including the dot, e.g. ".pxd"
+ @return: the file path if found, or None
+ """
+ assert not suffix or suffix[:1] == '.'
+ path_prefix = os.path.join(directory, filename)
+
+ matching_files = glob.glob(path_prefix + ".cython-*" + suffix)
+ path = path_prefix + suffix
+ if not os.path.exists(path):
+ path = None
+ best_match = (-1, path) # last resort, if we do not have versioned .pxd files
+
+ for path in matching_files:
+ versions = _parse_file_version(path)
+ if versions:
+ int_version = int(versions[0])
+ # Let's assume no duplicates.
+ if best_match[0] < int_version <= _current_version:
+ best_match = (int_version, path)
+ return best_match[1]
+
+
# file name encodings
def decode_filename(filename):
@@ -226,46 +345,33 @@
pass
return filename
-# support for source file encoding detection
-
-_match_file_encoding = re.compile(u"coding[:=]\s*([-\w.]+)").search
+# support for source file encoding detection
-def detect_file_encoding(source_filename):
- f = open_source_file(source_filename, encoding="UTF-8", error_handling='ignore')
- try:
- return detect_opened_file_encoding(f)
- finally:
- f.close()
+_match_file_encoding = re.compile(br"(\w*coding)[:=]\s*([-\w.]+)").search
-def detect_opened_file_encoding(f):
+def detect_opened_file_encoding(f, default='UTF-8'):
# PEPs 263 and 3120
- # Most of the time the first two lines fall in the first 250 chars,
+ # Most of the time the first two lines fall in the first couple of hundred chars,
# and this bulk read/split is much faster.
- lines = f.read(250).split(u"\n")
- if len(lines) > 1:
- m = _match_file_encoding(lines[0])
+ lines = ()
+ start = b''
+ while len(lines) < 3:
+ data = f.read(500)
+ start += data
+ lines = start.split(b"\n")
+ if not data:
+ break
+
+ m = _match_file_encoding(lines[0])
+ if m and m.group(1) != b'c_string_encoding':
+ return m.group(2).decode('iso8859-1')
+ elif len(lines) > 1:
+ m = _match_file_encoding(lines[1])
if m:
- return m.group(1)
- elif len(lines) > 2:
- m = _match_file_encoding(lines[1])
- if m:
- return m.group(1)
- else:
- return "UTF-8"
- # Fallback to one-char-at-a-time detection.
- f.seek(0)
- chars = []
- for i in range(2):
- c = f.read(1)
- while c and c != u'\n':
- chars.append(c)
- c = f.read(1)
- encoding = _match_file_encoding(u''.join(chars))
- if encoding:
- return encoding.group(1)
- return "UTF-8"
+ return m.group(2).decode('iso8859-1')
+ return default
def skip_bom(f):
@@ -278,32 +384,33 @@
f.seek(0)
-def open_source_file(source_filename, mode="r",
- encoding=None, error_handling=None):
- if encoding is None:
- # Most of the time the coding is unspecified, so be optimistic that
- # it's UTF-8.
- f = open_source_file(source_filename, encoding="UTF-8", mode=mode, error_handling='ignore')
- encoding = detect_opened_file_encoding(f)
- if encoding == "UTF-8" and error_handling == 'ignore':
+def open_source_file(source_filename, encoding=None, error_handling=None):
+ stream = None
+ try:
+ if encoding is None:
+ # Most of the time the encoding is not specified, so try hard to open the file only once.
+ f = io.open(source_filename, 'rb')
+ encoding = detect_opened_file_encoding(f)
f.seek(0)
- skip_bom(f)
- return f
+ stream = io.TextIOWrapper(f, encoding=encoding, errors=error_handling)
else:
- f.close()
+ stream = io.open(source_filename, encoding=encoding, errors=error_handling)
- if not os.path.exists(source_filename):
+ except OSError:
+ if os.path.exists(source_filename):
+ raise # File is there, but something went wrong reading from it.
+ # Allow source files to be in zip files etc.
try:
loader = __loader__
if source_filename.startswith(loader.archive):
- return open_source_from_loader(
+ stream = open_source_from_loader(
loader, source_filename,
encoding, error_handling)
except (NameError, AttributeError):
pass
- stream = io.open(source_filename, mode=mode,
- encoding=encoding, errors=error_handling)
+ if stream is None:
+ raise FileNotFoundError(source_filename)
skip_bom(stream)
return stream
@@ -355,7 +462,8 @@
@cached_function
def get_cython_cache_dir():
- """get the cython cache dir
+ r"""
+ Return the base directory containing Cython's caches.
Priority:
@@ -383,48 +491,64 @@
return os.path.expanduser(os.path.join('~', '.cython'))
-@contextmanager
+@try_finally_contextmanager
def captured_fd(stream=2, encoding=None):
- pipe_in = t = None
orig_stream = os.dup(stream) # keep copy of original stream
try:
- pipe_in, pipe_out = os.pipe()
- os.dup2(pipe_out, stream) # replace stream by copy of pipe
- try:
- os.close(pipe_out) # close original pipe-out stream
- data = []
-
- def copy():
- try:
- while True:
- d = os.read(pipe_in, 1000)
- if d:
- data.append(d)
- else:
- break
- finally:
- os.close(pipe_in)
+ with tempfile.TemporaryFile(mode="a+b") as temp_file:
+ def read_output(_output=[b'']):
+ if not temp_file.closed:
+ temp_file.seek(0)
+ _output[0] = temp_file.read()
+ return _output[0]
+ os.dup2(temp_file.fileno(), stream) # replace stream by copy of pipe
def get_output():
- output = b''.join(data)
- if encoding:
- output = output.decode(encoding)
- return output
-
- from threading import Thread
- t = Thread(target=copy)
- t.daemon = True # just in case
- t.start()
+ result = read_output()
+ return result.decode(encoding) if encoding else result
+
yield get_output
- finally:
+ # note: @contextlib.contextmanager requires try-finally here
os.dup2(orig_stream, stream) # restore original stream
- if t is not None:
- t.join()
+ read_output() # keep the output in case it's used after closing the context manager
finally:
os.close(orig_stream)
-def print_bytes(s, end=b'\n', file=sys.stdout, flush=True):
+def get_encoding_candidates():
+ candidates = [sys.getdefaultencoding()]
+ for stream in (sys.stdout, sys.stdin, sys.__stdout__, sys.__stdin__):
+ encoding = getattr(stream, 'encoding', None)
+ # encoding might be None (e.g. somebody redirects stdout):
+ if encoding is not None and encoding not in candidates:
+ candidates.append(encoding)
+ return candidates
+
+
+def prepare_captured(captured):
+ captured_bytes = captured.strip()
+ if not captured_bytes:
+ return None
+ for encoding in get_encoding_candidates():
+ try:
+ return captured_bytes.decode(encoding)
+ except UnicodeDecodeError:
+ pass
+ # last resort: print at least the readable ascii parts correctly.
+ return captured_bytes.decode('latin-1')
+
+
+def print_captured(captured, output, header_line=None):
+ captured = prepare_captured(captured)
+ if captured:
+ if header_line:
+ output.write(header_line)
+ output.write(captured)
+
+
+def print_bytes(s, header_text=None, end=b'\n', file=sys.stdout, flush=True):
+ if header_text:
+ file.write(header_text) # note: text! => file.write() instead of out.write()
file.flush()
try:
out = file.buffer # Py3
@@ -436,17 +560,24 @@
if flush:
out.flush()
-class LazyStr:
- def __init__(self, callback):
- self.callback = callback
- def __str__(self):
- return self.callback()
- def __repr__(self):
- return self.callback()
- def __add__(self, right):
- return self.callback() + right
- def __radd__(self, left):
- return left + self.callback()
+
+class OrderedSet(object):
+ def __init__(self, elements=()):
+ self._list = []
+ self._set = set()
+ self.update(elements)
+
+ def __iter__(self):
+ return iter(self._list)
+
+ def update(self, elements):
+ for e in elements:
+ self.add(e)
+
+ def add(self, e):
+ if e not in self._set:
+ self._list.append(e)
+ self._set.add(e)
# Class decorator that adds a metaclass and recreates the class with it.
@@ -465,3 +596,33 @@
orig_vars.pop('__weakref__', None)
return metaclass(cls.__name__, cls.__bases__, orig_vars)
return wrapper
+
+
+def raise_error_if_module_name_forbidden(full_module_name):
+ # it is bad idea to call the pyx-file cython.pyx, so fail early
+ if full_module_name == 'cython' or full_module_name.startswith('cython.'):
+ raise ValueError('cython is a special module, cannot be used as a module name')
+
+
+def build_hex_version(version_string):
+ """
+ Parse and translate '4.3a1' into the readable hex representation '0x040300A1' (like PY_VERSION_HEX).
+ """
+ # First, parse '4.12a1' into [4, 12, 0, 0xA01].
+ digits = []
+ release_status = 0xF0
+ for digit in re.split('([.abrc]+)', version_string):
+ if digit in ('a', 'b', 'rc'):
+ release_status = {'a': 0xA0, 'b': 0xB0, 'rc': 0xC0}[digit]
+ digits = (digits + [0, 0])[:3] # 1.2a1 -> 1.2.0a1
+ elif digit != '.':
+ digits.append(int(digit))
+ digits = (digits + [0] * 3)[:4]
+ digits[3] += release_status
+
+ # Then, build a single hex value, two hex digits per version part.
+ hexversion = 0
+ for digit in digits:
+ hexversion = (hexversion << 8) + digit
+
+ return '0x%08X' % hexversion
diff -Nru cython-0.20.1+1~201611251650-6686/debian/bzr-builder.manifest cython-0.20.1+1~202203241016-9537/debian/bzr-builder.manifest
--- cython-0.20.1+1~201611251650-6686/debian/bzr-builder.manifest 2016-11-25 16:50:58.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/debian/bzr-builder.manifest 2022-03-24 10:16:48.000000000 +0000
@@ -1,3 +1,3 @@
-# bzr-builder format 0.3 deb-version 0.20.1+1~201611251650-6686-pkg8
-lp:~cython-dev/cython/master revid:git-v1:15a40e21bdc53f9421c0d90eb0b54dcf6c796e39
+# bzr-builder format 0.3 deb-version 0.20.1+1~202203241016-9537-pkg8
+lp:~cython-dev/cython/master revid:git-v1:d85cbf676dd7bec80fbfc23d038575a21859ac87
nest-part packaging lp:~cython-dev/cython/packaging py2_and_py3 debian revid:thopiekar@home-20131130113131-9q4vuseaqyfh9sh0
diff -Nru cython-0.20.1+1~201611251650-6686/debian/changelog cython-0.20.1+1~202203241016-9537/debian/changelog
--- cython-0.20.1+1~201611251650-6686/debian/changelog 2016-11-25 16:50:58.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/debian/changelog 2022-03-24 10:16:48.000000000 +0000
@@ -1,8 +1,8 @@
-cython (0.20.1+1~201611251650-6686-pkg8~ubuntu14.04.1) trusty; urgency=low
+cython (0.20.1+1~202203241016-9537-pkg8~ubuntu14.04.1) trusty; urgency=low
* Auto build.
- -- Thomas Karl Pietrowski Fri, 25 Nov 2016 16:50:58 +0000
+ -- Thomas Karl Pietrowski Thu, 24 Mar 2022 10:16:48 +0000
cython (0.17.x) experimental; urgency=low
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/bpnn3.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/bpnn3.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/bpnn3.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/bpnn3.py 2022-03-24 10:16:46.000000000 +0000
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Back-Propagation Neural Networks
-#
-# Written in Python. See http://www.python.org/
+#
+# Written in Python. See https://www.python.org/
#
# Neil Schemenauer
@@ -26,10 +26,10 @@
return m
class NN(object):
-# print 'class NN'
+# print 'class NN'
def __init__(self, ni, nh, no):
# number of input, hidden, and output nodes
- self.ni = ni + 1 # +1 for bias node
+ self.ni = ni + 1 # +1 for bias node
self.nh = nh
self.no = no
@@ -37,11 +37,11 @@
self.ai = [1.0]*self.ni
self.ah = [1.0]*self.nh
self.ao = [1.0]*self.no
-
+
# create weights
self.wi = makeMatrix(self.ni, self.nh)
self.wo = makeMatrix(self.nh, self.no)
- # set them to random vaules
+ # set them to random values
for i in range(self.ni):
for j in range(self.nh):
self.wi[i][j] = rand(-2.0, 2.0)
@@ -49,7 +49,7 @@
for k in range(self.no):
self.wo[j][k] = rand(-2.0, 2.0)
- # last change in weights for momentum
+ # last change in weights for momentum
self.ci = makeMatrix(self.ni, self.nh)
self.co = makeMatrix(self.nh, self.no)
@@ -67,7 +67,7 @@
for j in range(self.nh):
sum = 0.0
for i in range(self.ni):
- sum = sum + self.ai[i] * self.wi[i][j]
+ sum = sum + self.ai[i] * self.wi[i][j]
self.ah[j] = 1.0/(1.0+math.exp(-sum))
# output activations
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/chaos.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/chaos.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/chaos.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/chaos.py 2022-03-24 10:16:46.000000000 +0000
@@ -130,7 +130,7 @@
I = ii
break
else:
- I = dom[1] - 1
+ I = dom[1] - 1
return I
def __len__(self):
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/hexiom2.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/hexiom2.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/hexiom2.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/hexiom2.py 2022-03-24 10:16:46.000000000 +0000
@@ -17,7 +17,7 @@
def __init__(self, x, y):
self.x = x
self.y = y
-
+
DIRS = [ Dir(1, 0),
Dir(-1, 0),
Dir(0, 1),
@@ -35,7 +35,7 @@
FIRST_STRATEGY = 3
MAX_NEIGHBORS_STRATEGY = 4
MIN_NEIGHBORS_STRATEGY = 5
-
+
def __init__(self, count, empty=False):
self.count = count
self.cells = None if empty else [[0, 1, 2, 3, 4, 5, 6, EMPTY] for i in range(count)]
@@ -60,11 +60,11 @@
return True
else:
return False
-
+
def remove_all(self, v):
for i in range(self.count):
self.remove(i, v)
-
+
def remove_unfixed(self, v):
changed = False
for i in range(self.count):
@@ -72,7 +72,7 @@
if self.remove(i, v):
changed = True
return changed
-
+
def filter_tiles(self, tiles):
for v in range(8):
if tiles[v] == 0:
@@ -206,14 +206,14 @@
def contains_pos(self, pos):
return pos in self.nodes_by_pos
-
+
def get_by_pos(self, pos):
return self.nodes_by_pos[pos]
def get_by_id(self, id):
return self.nodes_by_id[id]
-
+
##################################
class Pos(object):
def __init__(self, hex, tiles, done = None):
@@ -223,7 +223,7 @@
def clone(self):
return Pos(self.hex, self.tiles, self.done.clone())
-
+
##################################
@cython.locals(pos=Pos, i=cython.long, v=cython.int,
@@ -260,7 +260,7 @@
for cell in done.cells:
if len(cell) == 1:
left[cell[0]] -= 1
-
+
for v in range(8):
# If there is none, remove the possibility from all tiles
if (pos.tiles[v] > 0) and (left[v] == 0):
@@ -276,7 +276,7 @@
if (not done.already_done(i)) and (v in cell):
done.set_done(i, v)
changed = True
-
+
# Force empty or non-empty around filled cells
filled_cells = (range(done.count) if last_move is None
else [last_move])
@@ -307,7 +307,7 @@
for u in unknown:
if done.remove(u, EMPTY):
changed = True
-
+
return changed
ASCENDING = 1
@@ -402,7 +402,7 @@
if (not all_done) or (not exact):
return OPEN
-
+
print_pos(pos, output)
return SOLVED
@@ -414,7 +414,7 @@
pass
else:
pos = prev
-
+
moves = find_moves(pos, strategy, order)
if len(moves) == 0:
return solved(pos, output)
@@ -481,12 +481,12 @@
else:
inctile = int(tile)
tiles[inctile] += 1
- # Look for locked tiles
+ # Look for locked tiles
if tile[0] == "+":
print("Adding locked tile: %d at pos %d, %d, id=%d" %
(inctile, x, y, hex.get_by_pos((x, y)).id))
done.set_done(hex.get_by_pos((x, y)).id, inctile)
-
+
linei += 1
for y in range(1, size):
ry = size - 1 + y
@@ -500,7 +500,7 @@
else:
inctile = int(tile)
tiles[inctile] += 1
- # Look for locked tiles
+ # Look for locked tiles
if tile[0] == "+":
print("Adding locked tile: %d at pos %d, %d, id=%d" %
(inctile, x, ry, hex.get_by_pos((x, ry)).id))
@@ -530,13 +530,13 @@
output = StringIO()
solve_file(f, strategy, order, output)
expected = """\
- 3 4 3 2
- 3 4 4 . 3
- 2 . . 3 4 3
-2 . 1 . 3 . 2
- 3 3 . 2 . 2
- 3 . 2 . 2
- 2 2 . 1
+ 3 4 3 2
+ 3 4 4 . 3
+ 2 . . 3 4 3
+2 . 1 . 3 . 2
+ 3 3 . 2 . 2
+ 3 . 2 . 2
+ 2 2 . 1
"""
if output.getvalue() != expected:
raise AssertionError("got a wrong answer:\n%s" % output.getvalue())
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/meteor_contest.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/meteor_contest.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/meteor_contest.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/meteor_contest.py 2022-03-24 10:16:46.000000000 +0000
@@ -64,7 +64,7 @@
def get_puzzle(w=w, h=h):
- board = [E*x + S*y + (y%2) for y in range(h) for x in range(w)]
+ board = [E*x + S*y + (y % 2) for y in range(h) for x in range(w)]
cti = dict((board[i], i) for i in range(len(board)))
idos = [[E, E, E, SE], # incremental direction offsets
@@ -143,7 +143,7 @@
tk = time.time()
times.append(tk - t0)
return times
-
+
if __name__ == "__main__":
parser = optparse.OptionParser(
usage="%prog [options]",
@@ -152,4 +152,3 @@
options, args = parser.parse_args()
util.run_benchmark(options, options.num_runs, main)
-
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/nqueens.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/nqueens.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/nqueens.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/nqueens.py 2022-03-24 10:16:46.000000000 +0000
@@ -43,7 +43,7 @@
else:
return
-# From http://code.activestate.com/recipes/576647/
+# From https://code.activestate.com/recipes/576647/
@cython.locals(queen_count=int, i=int, vec=list)
def n_queens(queen_count):
"""N-Queens solver.
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/richards.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/richards.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/richards.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/richards.py 2022-03-24 10:16:46.000000000 +0000
@@ -102,13 +102,13 @@
self.task_waiting = False
self.task_holding = False
return self
-
+
def waitingWithPacket(self):
self.packet_pending = True
self.task_waiting = True
self.task_holding = False
return self
-
+
def isPacketPending(self):
return self.packet_pending
@@ -233,7 +233,7 @@
if t is None:
raise Exception("Bad task id %d" % id)
return t
-
+
# DeviceTask
@@ -307,7 +307,7 @@
else:
i.control = i.control//2 ^ 0xd008
return self.release(I_DEVB)
-
+
# WorkTask
@@ -333,7 +333,7 @@
pkt.ident = dest
pkt.datum = 0
- for i in BUFSIZE_RANGE: # range(BUFSIZE)
+ for i in BUFSIZE_RANGE: # range(BUFSIZE)
w.count += 1
if w.count > 26:
w.count = 1
@@ -382,10 +382,10 @@
wkq = Packet(wkq , I_DEVB, K_DEV)
HandlerTask(I_HANDLERB, 3000, wkq, TaskState().waitingWithPacket(), HandlerTaskRec())
- wkq = None;
- DeviceTask(I_DEVA, 4000, wkq, TaskState().waiting(), DeviceTaskRec());
- DeviceTask(I_DEVB, 5000, wkq, TaskState().waiting(), DeviceTaskRec());
-
+ wkq = None
+ DeviceTask(I_DEVA, 4000, wkq, TaskState().waiting(), DeviceTaskRec())
+ DeviceTask(I_DEVB, 5000, wkq, TaskState().waiting(), DeviceTaskRec())
+
schedule()
if taskWorkArea.holdCount == 9297 and taskWorkArea.qpktCount == 23246:
@@ -431,7 +431,7 @@
for it in item.__dict__.values():
if isinstance(it, types.FunctionType):
pypyjit.enable(it.func_code)
-
+
if __name__ == '__main__':
import sys
if len(sys.argv) >= 2:
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/benchmarks/spectralnorm.py cython-0.20.1+1~202203241016-9537/Demos/benchmarks/spectralnorm.py
--- cython-0.20.1+1~201611251650-6686/Demos/benchmarks/spectralnorm.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/benchmarks/spectralnorm.py 2022-03-24 10:16:46.000000000 +0000
@@ -11,28 +11,28 @@
import util
import optparse
-def eval_A (i, j):
+def eval_A(i, j):
return 1.0 / ((i + j) * (i + j + 1) / 2 + i + 1)
-def eval_A_times_u (u):
+def eval_A_times_u(u):
return [ part_A_times_u(i,u) for i in range(len(u)) ]
-def eval_At_times_u (u):
+def eval_At_times_u(u):
return [ part_At_times_u(i,u) for i in range(len(u)) ]
-def eval_AtA_times_u (u):
- return eval_At_times_u (eval_A_times_u (u))
+def eval_AtA_times_u(u):
+ return eval_At_times_u(eval_A_times_u(u))
def part_A_times_u(i, u):
partial_sum = 0
for j, u_j in enumerate(u):
- partial_sum += eval_A (i, j) * u_j
+ partial_sum += eval_A(i, j) * u_j
return partial_sum
def part_At_times_u(i, u):
partial_sum = 0
for j, u_j in enumerate(u):
- partial_sum += eval_A (j, i) * u_j
+ partial_sum += eval_A(j, i) * u_j
return partial_sum
DEFAULT_N = 130
@@ -43,19 +43,19 @@
t0 = time()
u = [1] * DEFAULT_N
- for dummy in range (10):
- v = eval_AtA_times_u (u)
- u = eval_AtA_times_u (v)
+ for dummy in range(10):
+ v = eval_AtA_times_u(u)
+ u = eval_AtA_times_u(v)
vBv = vv = 0
- for ue, ve in zip (u, v):
+ for ue, ve in zip(u, v):
vBv += ue * ve
vv += ve * ve
tk = time()
times.append(tk - t0)
return times
-
+
if __name__ == "__main__":
parser = optparse.OptionParser(
usage="%prog [options]",
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/callback/cheese.pyx cython-0.20.1+1~202203241016-9537/Demos/callback/cheese.pyx
--- cython-0.20.1+1~201611251650-6686/Demos/callback/cheese.pyx 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/callback/cheese.pyx 2022-03-24 10:16:46.000000000 +0000
@@ -8,7 +8,7 @@
def find(f):
find_cheeses(callback, f)
-
+
cdef void callback(char *name, void *f):
(f)(name.decode('utf-8'))
-
+
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/callback/README.rst cython-0.20.1+1~202203241016-9537/Demos/callback/README.rst
--- cython-0.20.1+1~201611251650-6686/Demos/callback/README.rst 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/callback/README.rst 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,12 @@
+This example demonstrates how you can wrap a C API
+that has a callback interface, so that you can
+pass Python functions to it as callbacks.
+
+The files ``cheesefinder.h`` and ``cheesefinder.c``
+represent the C library to be wrapped.
+
+The file ``cheese.pyx`` is the Cython module
+which wraps it.
+
+The file ``run_cheese.py`` demonstrates how to
+call the wrapper.
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/callback/README.txt cython-0.20.1+1~202203241016-9537/Demos/callback/README.txt
--- cython-0.20.1+1~201611251650-6686/Demos/callback/README.txt 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/callback/README.txt 1970-01-01 00:00:00.000000000 +0000
@@ -1,12 +0,0 @@
-This example demonstrates how you can wrap a C API
-that has a callback interface, so that you can
-pass Python functions to it as callbacks.
-
-The files cheesefinder.h and cheesefinder.c
-represent the C library to be wrapped.
-
-The file cheese.pyx is the Pyrex module
-which wraps it.
-
-The file run_cheese.py demonstrates how to
-call the wrapper.
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/callback/run_cheese.py cython-0.20.1+1~202203241016-9537/Demos/callback/run_cheese.py
--- cython-0.20.1+1~201611251650-6686/Demos/callback/run_cheese.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/callback/run_cheese.py 2022-03-24 10:16:46.000000000 +0000
@@ -4,5 +4,3 @@
print("Found cheese: " + name)
cheese.find(report_cheese)
-
-
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/embed/Makefile cython-0.20.1+1~202203241016-9537/Demos/embed/Makefile
--- cython-0.20.1+1~201611251650-6686/Demos/embed/Makefile 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/embed/Makefile 2022-03-24 10:16:46.000000000 +0000
@@ -1,6 +1,7 @@
# Makefile for creating our standalone Cython program
PYTHON := python
PYVERSION := $(shell $(PYTHON) -c "import sys; print(sys.version[:3])")
+PYPREFIX := $(shell $(PYTHON) -c "import sys; print(sys.prefix)")
INCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc())")
PLATINCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc(plat_specific=True))")
@@ -14,6 +15,23 @@
LIBS := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LIBS'))")
SYSLIBS := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('SYSLIBS'))")
+.PHONY: paths all clean test
+
+paths:
+ @echo "PYTHON=$(PYTHON)"
+ @echo "PYVERSION=$(PYVERSION)"
+ @echo "PYPREFIX=$(PYPREFIX)"
+ @echo "INCDIR=$(INCDIR)"
+ @echo "PLATINCDIR=$(PLATINCDIR)"
+ @echo "LIBDIR1=$(LIBDIR1)"
+ @echo "LIBDIR2=$(LIBDIR2)"
+ @echo "PYLIB=$(PYLIB)"
+ @echo "CC=$(CC)"
+ @echo "LINKCC=$(LINKCC)"
+ @echo "LINKFORSHARED=$(LINKFORSHARED)"
+ @echo "LIBS=$(LIBS)"
+ @echo "SYSLIBS=$(SYSLIBS)"
+
embedded: embedded.o
$(LINKCC) -o $@ $^ -L$(LIBDIR1) -L$(LIBDIR2) -l$(PYLIB) $(LIBS) $(SYSLIBS) $(LINKFORSHARED)
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/embed/README cython-0.20.1+1~202203241016-9537/Demos/embed/README
--- cython-0.20.1+1~201611251650-6686/Demos/embed/README 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/embed/README 1970-01-01 00:00:00.000000000 +0000
@@ -1,5 +0,0 @@
-This example demonstrates how Cython-generated code
-can be called directly from a main program written in C.
-
-The Windows makefiles were contributed by
-Duncan Booth .
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/embed/README.rst cython-0.20.1+1~202203241016-9537/Demos/embed/README.rst
--- cython-0.20.1+1~201611251650-6686/Demos/embed/README.rst 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/embed/README.rst 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,5 @@
+This example demonstrates how Cython-generated code
+can be called directly from a main program written in C.
+
+The Windows makefiles were contributed by
+Duncan Booth: Duncan.Booth@SuttonCourtenay.org.uk.
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/freeze/README.rst cython-0.20.1+1~202203241016-9537/Demos/freeze/README.rst
--- cython-0.20.1+1~201611251650-6686/Demos/freeze/README.rst 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/freeze/README.rst 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,111 @@
+NAME
+====
+
+**cython_freeze** - create a C file for embedding Cython modules
+
+
+SYNOPSIS
+========
+::
+
+ cython_freeze [-o outfile] [-p] module [...]
+
+
+DESCRIPTION
+===========
+
+**cython_freeze** generates a C source file to embed a Python interpreter
+with one or more Cython modules built in. This allows one to create a single
+executable from Cython code, without having to have separate shared objects
+for each Cython module. A major advantage of this approach is that it allows
+debugging with gprof(1), which does not work with shared objects.
+
+Unless ``-p`` is given, the first module's ``__name__`` is set to
+``"__main__"`` and is imported on startup; if ``-p`` is given, a normal Python
+interpreter is built, with the given modules built into the binary.
+
+Note that this method differs from ``cython --embed``. The ``--embed`` options
+modifies the resulting C source file to include a ``main()`` function, so it
+can only be used on a single Cython module. The advantage ``--embed`` is
+simplicity. This module, on the other hand, can be used with multiple
+modules, but it requires another C source file to be created.
+
+
+OPTIONS
+=======
+::
+
+ -o FILE, --outfile=FILE write output to FILE instead of standard output
+ -p, --pymain do not automatically run the first module as __main__
+
+
+EXAMPLE
+=======
+
+In the ``Demos/freeze`` directory, there exist two Cython modules:
+
+* ``lcmath.pyx``: A module that interfaces with the -lm library.
+
+* ``combinatorics.pyx``: A module that implements n-choose-r using lcmath.
+
+Both modules have the Python idiom ``if __name__ == "__main__"``, which only
+execute if that module is the "main" module. If run as main, lcmath prints the
+factorial of the argument, while combinatorics prints n-choose-r.
+
+The provided Makefile creates an executable, *nCr*, using combinatorics as the
+"main" module. It basically performs the following (ignoring the compiler
+flags)::
+
+ $ cython_freeze combinatorics lcmath > nCr.c
+ $ cython combinatorics.pyx
+ $ cython lcmath.pyx
+ $ gcc -c nCr.c
+ $ gcc -c combinatorics.c
+ $ gcc -c lcmath.c
+ $ gcc nCr.o combinatorics.o lcmath.o -o nCr
+
+Because the combinatorics module was listed first, its ``__name__`` is set
+to ``"__main__"``, while lcmath's is set to ``"lcmath"``. The executable now
+contains a Python interpreter and both Cython modules. ::
+
+ $ ./nCr
+ USAGE: ./nCr n r
+ Prints n-choose-r.
+ $ ./nCr 15812351235 12
+ 5.10028093999e+113
+
+You may wish to build a normal Python interpreter, rather than having one
+module as "main". This may happen if you want to use your module from an
+interactive shell or from another script, yet you still want it statically
+linked so you can profile it with gprof. To do this, add the ``--pymain``
+flag to ``cython_freeze``. In the Makefile, the *python* executable is built
+like this. ::
+
+ $ cython_freeze --pymain combinatorics lcmath -o python.c
+ $ gcc -c python.c
+ $ gcc python.o combinatorics.o lcmath.o -o python
+
+Now ``python`` is a normal Python interpreter, but the lcmath and combinatorics
+modules will be built into the executable. ::
+
+ $ ./python
+ Python 2.6.2 (release26-maint, Apr 19 2009, 01:58:18)
+ [GCC 4.3.3] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import lcmath
+ >>> lcmath.factorial(155)
+ 4.7891429014634364e+273
+
+
+PREREQUISITES
+=============
+
+Cython 0.11.2 (or newer, assuming the API does not change)
+
+
+SEE ALSO
+========
+
+* `Python `_
+* `Cython `_
+* `freeze.py `_
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/freeze/README.txt cython-0.20.1+1~202203241016-9537/Demos/freeze/README.txt
--- cython-0.20.1+1~201611251650-6686/Demos/freeze/README.txt 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/freeze/README.txt 1970-01-01 00:00:00.000000000 +0000
@@ -1,111 +0,0 @@
-NAME
-====
-
-cython_freeze - create a C file for embedding Cython modules
-
-
-SYNOPSIS
-========
-
-cython_freeze [-o outfile] [-p] module [...]
-
-
-DESCRIPTION
-===========
-
-**cython_freeze** generates a C source file to embed a Python interpreter
-with one or more Cython modules built in. This allows one to create a single
-executable from Cython code, without having to have separate shared objects
-for each Cython module. A major advantage of this approach is that it allows
-debuging with gprof(1), which does not work with shared objects.
-
-Unless ``-p`` is given, the first module's ``__name__`` is set to
-``"__main__"`` and is imported on startup; if ``-p`` is given, a normal Python
-interpreter is built, with the given modules built into the binary.
-
-Note that this method differs from ``cython --embed``. The ``--embed`` options
-modifies the resulting C source file to include a ``main()`` function, so it
-can only be used on a single Cython module. The advantage ``--embed`` is
-simplicity. This module, on the other hand, can be used with multiple
-modules, but it requires another C source file to be created.
-
-
-OPTIONS
-=======
-
--o FILE, --outfile=FILE write output to FILE instead of standard output
--p, --pymain do not automatically run the first module as __main__
-
-
-EXAMPLE
-=======
-
-In the Demos/freeze directory, there exist two Cython modules:
-
-lcmath.pyx
- A module that interfaces with the -lm library.
-
-combinatorics.pyx
- A module that implements n-choose-r using lcmath.
-
-Both modules have the Python idiom ``if __name__ == "__main__"``, which only
-execute if that module is the "main" module. If run as main, lcmath prints the
-factorial of the argument, while combinatorics prints n-choose-r.
-
-The provided Makefile creates an executable, *nCr*, using combinatorics as the
-"main" module. It basically performs the following (ignoring the compiler
-flags)::
-
- $ cython_freeze combinatorics lcmath > nCr.c
- $ cython combinatorics.pyx
- $ cython lcmath.pyx
- $ gcc -c nCr.c
- $ gcc -c combinatorics.c
- $ gcc -c lcmath.c
- $ gcc nCr.o combinatorics.o lcmath.o -o nCr
-
-Because the combinatorics module was listed first, its ``__name__`` is set
-to ``"__main__"``, while lcmath's is set to ``"lcmath"``. The executable now
-contains a Python interpreter and both Cython modules. ::
-
- $ ./nCr
- USAGE: ./nCr n r
- Prints n-choose-r.
- $ ./nCr 15812351235 12
- 5.10028093999e+113
-
-You may wish to build a normal Python interpreter, rather than having one
-module as "main". This may happen if you want to use your module from an
-interactive shell or from another script, yet you still want it statically
-linked so you can profile it with gprof. To do this, add the ``--pymain``
-flag to ``cython_freeze``. In the Makefile, the *python* executable is built
-like this. ::
-
- $ cython_freeze --pymain combinatorics lcmath -o python.c
- $ gcc -c python.c
- $ gcc python.o combinatorics.o lcmath.o -o python
-
-Now ``python`` is a normal Python interpreter, but the lcmath and combinatorics
-modules will be built into the executable. ::
-
- $ ./python
- Python 2.6.2 (release26-maint, Apr 19 2009, 01:58:18)
- [GCC 4.3.3] on linux2
- Type "help", "copyright", "credits" or "license" for more information.
- >>> import lcmath
- >>> lcmath.factorial(155)
- 4.7891429014634364e+273
-
-
-PREREQUISITES
-=============
-
-Cython 0.11.2 (or newer, assuming the API does not change)
-
-
-SEE ALSO
-========
-
-* `Python `_
-* `Cython `_
-* `freeze.py `_
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/libraries/setup.py cython-0.20.1+1~202203241016-9537/Demos/libraries/setup.py
--- cython-0.20.1+1~201611251650-6686/Demos/libraries/setup.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/libraries/setup.py 2022-03-24 10:16:46.000000000 +0000
@@ -18,7 +18,7 @@
print("Error building external library, please create libmymath.a manually.")
sys.exit(1)
-# Here is how to use the library built above.
+# Here is how to use the library built above.
ext_modules = cythonize([
Extension("call_mymath",
sources=["call_mymath.pyx"],
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/overflow_perf_run.py cython-0.20.1+1~202203241016-9537/Demos/overflow_perf_run.py
--- cython-0.20.1+1~201611251650-6686/Demos/overflow_perf_run.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/overflow_perf_run.py 2022-03-24 10:16:46.000000000 +0000
@@ -16,7 +16,7 @@
print(func.__name__)
for type in ['int', 'unsigned int', 'long long', 'unsigned long long', 'object']:
if func == most_orthogonal:
- if type == 'object' or np == None:
+ if type == 'object' or np is None:
continue
type_map = {'int': 'int32', 'unsigned int': 'uint32', 'long long': 'int64', 'unsigned long long': 'uint64'}
shape = N, 3
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/pyprimes.py cython-0.20.1+1~202203241016-9537/Demos/pyprimes.py
--- cython-0.20.1+1~201611251650-6686/Demos/pyprimes.py 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/pyprimes.py 2022-03-24 10:16:46.000000000 +0000
@@ -5,9 +5,9 @@
while k < kmax:
i = 0
while i < k and n % p[i] != 0:
- i = i + 1
+ i += 1
if i == k:
p.append(n)
- k = k + 1
- n = n + 1
+ k += 1
+ n += 1
return p
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/Readme_demos.rst cython-0.20.1+1~202203241016-9537/Demos/Readme_demos.rst
--- cython-0.20.1+1~201611251650-6686/Demos/Readme_demos.rst 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/Readme_demos.rst 1970-01-01 00:00:00.000000000 +0000
@@ -1,35 +0,0 @@
-* To run demos do::
-
- cd Demos
- make test
-
- which runs run_primes.py, run_numeric_demo.py, run_spam.py,
- integrate_timing.py, callback/runcheese.py and embed/embedded
-
-* For other demos::
-
- cd libraries
- python setup.py build_ext --inplace
- python -c 'import call_mymath;print(call_mymath.call_sinc(1))'
-
- To run one of the benchmarks for 10 iterations to compare cython and python timings::
-
- cd benchmarks
- python setup.py build_ext --inplace
- python nqueens.py -n 10
- python -c 'import nqueens;print(nqueens.test_n_queens(10))'
-
- To demo cython/bin/cython_freeze::
-
- make
- ./nCr 10 5
- ./python
-
-* Build notes
-
- * benchmarks/chaos.py requires cython 0.24 or newer
-
- * embed and freeze work for python2, require cython 0.24 or higher
- for python 3.5
-
-
diff -Nru cython-0.20.1+1~201611251650-6686/Demos/README.rst cython-0.20.1+1~202203241016-9537/Demos/README.rst
--- cython-0.20.1+1~201611251650-6686/Demos/README.rst 1970-01-01 00:00:00.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Demos/README.rst 2022-03-24 10:16:46.000000000 +0000
@@ -0,0 +1,35 @@
+To run demos do::
+
+ cd Demos
+ make test
+
+which runs ``run_primes.py``, ``run_numeric_demo.py``, ``run_spam.py``,
+``integrate_timing.py``, ``callback/runcheese.py`` and ``embed/embedded``
+
+For other demos::
+
+ cd libraries
+ python setup.py build_ext --inplace
+ python -c 'import call_mymath;print(call_mymath.call_sinc(1))'
+
+To run one of the benchmarks for 10 iterations to compare cython and python timings::
+
+ cd benchmarks
+ python setup.py build_ext --inplace
+ python nqueens.py -n 10
+ python -c 'import nqueens;print(nqueens.test_n_queens(10))'
+
+To demo ``cython/bin/cython_freeze``::
+
+ make
+ ./nCr 10 5
+ ./python
+
+* Build notes
+
+ * benchmarks/chaos.py requires cython 0.24 or newer
+
+ * embed and freeze work for python2, require cython 0.24 or higher
+ for python 3.5
+
+
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/About.html cython-0.20.1+1~202203241016-9537/Doc/About.html
--- cython-0.20.1+1~201611251650-6686/Doc/About.html 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/About.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,171 +0,0 @@
-
-
-
-
-
- About Cython
-
-
-
-
-
-
- Cython
-
-A language for writing Python extension modules
-
-
-
-What is Cython all about?
-Cython is a language specially designed for writing Python extension modules.
-It's designed to bridge the gap between the nice, high-level, easy-to-use
-world of Python and the messy, low-level world of C.
-You may be wondering why anyone would want a special language for this.
-Python is really easy to extend using C or C++, isn't it? Why not just
-write your extension modules in one of those languages?
-
Well, if you've ever written an extension module for Python, you'll
-know that things are not as easy as all that. First of all, there is a
-fair bit of boilerplate code to write before you can even get off the ground.
-Then you're faced with the problem of converting between Python and C data
-types. For the basic types such as numbers and strings this is not too
-bad, but anything more elaborate and you're into picking Python objects
-apart using the Python/C API calls, which requires you to be meticulous
-about maintaining reference counts, checking for errors at every step and
-cleaning up properly if anything goes wrong. Any mistakes and you have
-a nasty crash that's very difficult to debug.
-
Various tools have been developed to ease some of the burdens of producing
-extension code, of which perhaps SWIG
-is the best known. SWIG takes a definition file consisting of a mixture
-of C code and specialised declarations, and produces an extension module.
-It writes all the boilerplate for you, and in many cases you can use it
-without knowing about the Python/C API. But you need to use API calls if
-any substantial restructuring of the data is required between Python and
-C.
-
What's more, SWIG gives you no help at all if you want to create a new
-built-in Python type. It will generate pure-Python classes which
-wrap (in a slightly unsafe manner) pointers to C data structures, but creation
-of true extension types is outside its scope.
-
Another notable attempt at making it easier to extend Python is PyInline
-, inspired by a similar facility for Perl. PyInline lets you embed pieces
-of C code in the midst of a Python file, and automatically extracts them
-and compiles them into an extension. But it only converts the basic types
-automatically, and as with SWIG, it doesn't address the creation
-of new Python types.
-
Cython aims to go far beyond what any of these previous tools provides.
-Cython deals with the basic types just as easily as SWIG, but it also lets
-you write code to convert between arbitrary Python data structures and
-arbitrary C data structures, in a simple and natural way, without knowing
-anything about the Python/C API. That's right -- nothing at all !
-Nor do you have to worry about reference counting or error checking --
-it's all taken care of automatically, behind the scenes, just as it is
-in interpreted Python code. And what's more, Cython lets you define new
-built-in Python types just as easily as you can define new classes
-in Python.
-
Sound too good to be true? Read on and find out how it's done.
-
-The Basics of Cython
-The fundamental nature of Cython can be summed up as follows: Cython is
-Python with C data types .
-Cython is Python: Almost any piece of Python code is also valid
-Cython code. (There are a few limitations, but this approximation will serve
-for now.) The Cython compiler will convert it into C code which makes equivalent
-calls to the Python/C API. In this respect, Cython is similar to the former
-Python2C project (to which I would supply a reference except that it no
-longer seems to exist).
-
...with C data types. But Cython is much more than that, because
-parameters and variables can be declared to have C data types. Code which
-manipulates Python values and C values can be freely intermixed, with conversions
-occurring automatically wherever possible. Reference count maintenance
-and error checking of Python operations is also automatic, and the full
-power of Python's exception handling facilities, including the try-except
-and try-finally statements, is available to you -- even in the midst of
-manipulating C data.
-
Here's a small example showing some of what can be done. It's a routine
-for finding prime numbers. You tell it how many primes you want, and it
-returns them as a Python list.
-
primes.pyx
-
-
- 1 def primes(int kmax):
- 2 cdef int n, k, i
- 3 cdef int p[1000]
- 4 result = []
- 5 if kmax > 1000:
- 6 kmax = 1000
- 7 k = 0
- 8 n = 2
- 9 while k < kmax:
-10 i = 0
-11 while i < k and n % p[i] <> 0:
-12 i = i + 1
-13 if i == k:
-14 p[k] = n
-15 k = k + 1
-16 result.append(n)
-17 n = n + 1
-18 return result
-
-You'll see that it starts out just like a normal Python function definition,
-except that the parameter kmax is declared to be of type int
-. This means that the object passed will be converted to a C integer (or
-a TypeError will be raised if it can't be).
-Lines 2 and 3 use the cdef statement to define some local C variables.
-Line 4 creates a Python list which will be used to return the result. You'll
-notice that this is done exactly the same way it would be in Python. Because
-the variable result hasn't been given a type, it is assumed to hold
-a Python object.
-
Lines 7-9 set up for a loop which will test candidate numbers for primeness
-until the required number of primes has been found. Lines 11-12, which
-try dividing a candidate by all the primes found so far, are of particular
-interest. Because no Python objects are referred to, the loop is translated
-entirely into C code, and thus runs very fast.
-
When a prime is found, lines 14-15 add it to the p array for fast access
-by the testing loop, and line 16 adds it to the result list. Again, you'll
-notice that line 16 looks very much like a Python statement, and in fact
-it is, with the twist that the C parameter n is automatically converted
-to a Python object before being passed to the append method. Finally,
-at line 18, a normal Python return statement returns the result
-list.
-
Compiling primes.pyx with the Cython compiler produces an extension module
-which we can try out in the interactive interpreter as follows:
-
->>> import primes
->>> primes.primes(10)
-[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
->>>
-
-See, it works! And if you're curious about how much work Cython has saved
-you, take a look at the C code generated for this module
-.
-
-Language Details
-For more about the Cython language, see the Language
-Overview .
-
-Future Plans
-Cython is not finished. Substantial tasks remaining include:
-
-
-Support for certain Python language features which are planned but not
-yet implemented. See the Limitations
-section of the Language Overview for a current
-list.
-
-
-
-
-C++ support. This could be a very big can of worms - careful thought required
-before going there.
-
-
-
-
-Reading C/C++ header files directly would be very nice, but there are some
-severe problems that I will have to find solutions for first, such as what
-to do about preprocessor macros. My current thinking is to use a separate
-tool to convert .h files into Cython declarations, possibly with some manual
-intervention.
-
-
-
-
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/extension_types.html cython-0.20.1+1~202203241016-9537/Doc/extension_types.html
--- cython-0.20.1+1~201611251650-6686/Doc/extension_types.html 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/extension_types.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,444 +0,0 @@
-
-
-
- Extension Types
-
- Extension Types
-
- Contents
-
- Introduction
- As well as creating normal user-defined classes with the Python class
-statement, Cython also lets you create new built-in Python types, known as
-extension types . You define an extension type using the cdef class statement. Here's an example:
-cdef class Shrubbery: cdef int width, height
- def __init__(self, w, h):
- self.width = w
- self.height = h
- def describe(self):
- print "This shrubbery is",
-self.width, \
-
-"by", self.height, "cubits."
-
- As you can see, a Cython extension type definition looks a lot like a Python
- class definition. Within it, you use the def statement to define
-methods that can be called from Python code. You can even define many of
-the special methods such as __init__ as you would in Python.
-The main difference is that you can use the cdef statement to define
-attributes. The attributes may be Python objects (either generic or of a particular
-extension type), or they may be of any C data type. So you can use extension
-types to wrap arbitrary C data structures and provide a Python-like interface
-to them.
- Attributes
- Attributes of an extension type are stored directly in the object's C struct.
- The set of attributes is fixed at compile time; you can't add attributes
-to an extension type instance at run time simply by assigning to them, as
-you could with a Python class instance. (You can subclass the extension type
-in Python and add attributes to instances of the subclass, however.)
-There are two ways that attributes of an extension type can be accessed:
- by Python attribute lookup, or by direct access to the C struct from Cython
- code. Python code is only able to access attributes of an extension type
-by the first method, but Cython code can use either method.
- By default, extension type attributes are only accessible by direct access,
-not Python access, which means that they are not accessible from Python code.
-To make them accessible from Python code, you need to declare them as public or readonly . For example,
- cdef class Shrubbery:
- cdef public int width, height
- cdef readonly float depth
- makes the width and height attributes readable and writable
- from Python code, and the depth attribute readable but not writable.
-
-Note that you can only expose simple C types, such as ints, floats and
- strings, for Python access. You can also expose Python-valued attributes,
- although read-write exposure is only possible for generic Python attributes
- (of type object ). If the attribute is declared to be of an extension
- type, it must be exposed readonly .
- Note also that the public and readonly options apply
- only to Python access, not direct access. All the attributes of an
-extension type are always readable and writable by direct access.
- Howerver, for direct access to be possible, the Cython compiler must know
-that you have an instance of that type, and not just a generic Python object.
-It knows this already in the case of the "self" parameter of the methods of
-that type, but in other cases you will have to tell it by means of a declaration.
-For example,
- cdef widen_shrubbery(Shrubbery sh, extra_width):
- sh.width = sh.width + extra_width
- If you attempt to access an extension type attribute through a generic
-object reference, Cython will use a Python attribute lookup. If the attribute
-is exposed for Python access (using public or readonly )
-then this will work, but it will be much slower than direct access.
- Extension types and None
- When you declare a parameter or C variable as being of an extension type,
- Cython will allow it to take on the value None as well as values of its declared
-type. This is analogous to the way a C pointer can take on the value NULL,
-and you need to exercise the same caution because of it. There is no problem
-as long as you are performing Python operations on it, because full dynamic
-type checking will be applied. However, when you access C attributes of an
-extension type (as in the widen_shrubbery function above), it's up
-to you to make sure the reference you're using is not None -- in the interests
-of efficiency, Cython does not check this.
-You need to be particularly careful when exposing Python functions which
- take extension types as arguments. If we wanted to make widen_shrubbery
-a Python function, for example, if we simply wrote
- def widen_shrubbery(Shrubbery sh, extra_width): # This is
- sh.width = sh.width + extra_width
-# dangerous!
- then users of our module could crash it by passing None for the sh
-parameter.
-One way to fix this would be
- def widen_shrubbery(Shrubbery sh, extra_width):
- if sh is None:
- raise TypeError
- sh.width = sh.width + extra_width
- but since this is anticipated to be such a frequent requirement, Cython
-provides a more convenient way. Parameters of a Python function declared
-as an extension type can have a not None clause:
-def widen_shrubbery(Shrubbery sh not None, extra_width):
-
- sh.width = sh.width + extra_width
- Now the function will automatically check that sh is not None
-along with checking that it has the right type.
-Note, however that the not None clause can only be used
- in Python functions (defined with def ) and not C functions (defined
- with cdef ). If you need to check whether a parameter to a C function
- is None, you will need to do it yourself.
- Some more things to note:
-
- The self parameter of a method of an extension type is guaranteed
- never to be None.
-
-
- When comparing a value with None, keep in mind that, if x is a Python object, x is None and x is not None are very
-efficient because they translate directly to C pointer comparisons, whereas
- x == None and x != None , or simply using x as a boolean value (as in if x: ... ) will invoke Python operations
-and therefore be much slower.
-
- Special methods
- Although the principles are similar, there are substantial differences
-between many of the __xxx__ special methods of extension types and their
-Python counterparts. There is a separate page devoted to this subject, and you should read it carefully before attempting
-to use any special methods in your extension types.
- Properties
- There is a special syntax for defining properties in an extension
- class:
-cdef class Spam: property cheese:
- "A doc string can go
-here."
- def __get__(self):
-
-
-# This is called when the property is read.
-
-...
- def __set__(self, value):
-
-
-# This is called when the property is written.
-
-...
- def __del__(self):
-
-
-# This is called when the property is deleted.
-
-
- The __get__ , __set__ and __del__ methods are
-all optional; if they are omitted, an exception will be raised when the corresponding
-operation is attempted.
-Here's a complete example. It defines a property which adds to a list
-each time it is written to, returns the list when it is read, and empties
-the list when it is deleted.
-
-
-
-
- cheesy.pyx
- Test input
-
-
- cdef class CheeseShop:
- cdef object cheeses
- def __new__(self):
- self.cheeses = []
- property cheese:
- def __get__(self):
- return "We don't have: %s" % self.cheeses
-
- def __set__(self, value):
- self.cheeses.append(value)
-
- def __del__(self):
- del self.cheeses[:]
-
- from cheesy import CheeseShop
- shop = CheeseShop()
- print shop.cheese
- shop.cheese = "camembert"
- print shop.cheese
- shop.cheese = "cheddar"
- print shop.cheese
- del shop.cheese
- print shop.cheese
-
-
-
- Test output
-
-
- We don't have: []
- We don't have: ['camembert']
- We don't have: ['camembert', 'cheddar']
- We don't have: []
-
-
-
- Subclassing
- An extension type may inherit from a built-in type or another extension
-type:
-cdef class Parrot:
- ... cdef class Norwegian(Parrot):
- ...
-
-
- A complete definition of the base type must be available to Cython, so if
-the base type is a built-in type, it must have been previously declared as
-an extern extension type. If the base type is defined in another Cython
-module, it must either be declared as an extern extension type or imported
-using the cimport statement.
- An extension type can only have one base class (no multiple inheritance).
-
- Cython extension types can also be subclassed in Python. A Python class
- can inherit from multiple extension types provided that the usual Python
-rules for multiple inheritance are followed (i.e. the C layouts of all the
-base classes must be compatible).
-
- C methods
- Extension types can have C methods as well as Python methods. Like C functions,
-C methods are declared using cdef instead of def . C methods
-are "virtual", and may be overridden in derived extension types.
-
-
-
-
- pets.pyx
-
- Output
-
-
-
- cdef class Parrot:
-
- cdef void describe(self):
- print "This parrot is resting."
-
- cdef class Norwegian(Parrot):
-
- cdef void describe(self):
- Parrot.describe(self)
- print "Lovely plumage!"
-
-
- cdef Parrot p1, p2
- p1 = Parrot()
- p2 = Norwegian()
-print "p1:"
- p1.describe()
-print "p2:"
- p2.describe()
-
- p1:
-This parrot is resting.
-p2:
- This parrot is resting.
- Lovely plumage!
-
-
-
-
- The above example also illustrates that a C method can call an inherited
-C method using the usual Python technique, i.e.
-Parrot.describe(self)
-
- Forward-declaring extension types
- Extension types can be forward-declared, like struct and union types. This
- will be necessary if you have two extension types that need to refer to
-each other, e.g.
-cdef class Shrubbery # forward declaration cdef class Shrubber:
- cdef Shrubbery work_in_progress
- cdef class Shrubbery:
- cdef Shrubber creator
-
- If you are forward-declaring an exension type that has a base class, you
-must specify the base class in both the forward declaration and its subsequent
-definition, for example,
-cdef class A(B)
-
-...
-
-cdef class A(B):
- # attributes and methods
-
- Making extension types weak-referenceable By
-default, extension types do not support having weak references made to
-them. You can enable weak referencing by declaring a C attribute of
-type object called __weakref__ . For example,
-
-cdef class ExplodingAnimal:
- """This animal will self-destruct when it is
- no longer strongly referenced."""
-
- cdef object __weakref__
-
-
- Public and external extension types
-
- Extension types can be declared extern or public . An extern extension type declaration makes
-an extension type defined in external C code available to a Cython module.
-A public extension type declaration makes an extension type defined in a Cython module available to external C
-code.
- External extension types
- An extern extension type allows you to gain access to the internals
- of Python objects defined in the Python core or in a non-Cython extension
-module.
-NOTE: In Cython versions before 0.8, extern extension
- types were also used to reference extension types defined in another Cython
- module. While you can still do that, Cython 0.8 and later provides a better
- mechanism for this. See Sharing C Declarations Between
- Cython Modules .
- Here is an example which will let you get at the C-level members of the
-built-in complex object.
-cdef extern from "complexobject.h": struct Py_complex:
- double real
- double imag
- ctypedef class __builtin__.complex [object PyComplexObject]:
-
- cdef Py_complex cval
-
- # A function which uses the above type
- def spam(complex c):
- print "Real:", c.cval.real
- print "Imag:", c.cval.imag
-
- Some important things to note are:
-
- In this example, ctypedef class has been used. This is because,
- in the Python header files, the PyComplexObject struct is declared
- with
-
- ctypedef struct {
- ...
- } PyComplexObject;
-
-
- As well as the name of the extension type, the module in which
-its type object can be found is also specified. See the implicit importing section below.
-
-
- When declaring an external extension type, you don't declare
-any methods. Declaration of methods is not required in order to call them,
-because the calls are Python method calls. Also, as with structs and unions,
-if your extension class declaration is inside a cdef extern from block,
- you only need to declare those C members which you wish to access.
-
- Implicit importing
- Backwards Incompatibility Note :
-You will have to update any pre-0.8 Cython modules you have which use extern
-extension types. I apologise for this, but for complicated reasons it proved
- to be too difficult to continue supporting the old way of doing these while
- introducing the new features that I wanted.
- Cython 0.8 and later requires you to include a module name in an extern
-extension class declaration, for example,
-cdef extern class MyModule.Spam:
- ...
- The type object will be implicitly imported from the specified module and
- bound to the corresponding name in this module. In other words, in this
-example an implicit
-
- from MyModule import Spam
-
- statement will be executed at module load time.
-The module name can be a dotted name to refer to a module inside a package
- hierarchy, for example,
- cdef extern class My.Nested.Package.Spam:
- ...
- You can also specify an alternative name under which to import the type
-using an as clause, for example,
-
- cdef extern class My.Nested.Package.Spam as Yummy:
- ...
- which corresponds to the implicit import statement
-
- from My.Nested.Package import Spam as Yummy
-
- Type names vs. constructor names
- Inside a Cython module, the name of an extension type serves two distinct
- purposes. When used in an expression, it refers to a module-level global
-variable holding the type's constructor (i.e. its type-object). However,
-it can also be used as a C type name to declare variables, arguments and
-return values of that type.
-When you declare
- cdef extern class MyModule.Spam:
- ...
- the name Spam serves both these roles. There may be other names
- by which you can refer to the constructor, but only Spam can be
-used as a type name. For example, if you were to explicity import MyModule ,
- you could use MyModule.Spam() to create a Spam instance, but you
- wouldn't be able to use MyModule.Spam as a type name.
-When an as clause is used, the name specified in the as
-clause also takes over both roles. So if you declare
- cdef extern class MyModule.Spam as Yummy:
- ...
- then Yummy becomes both the type name and a name for the constructor.
- Again, there are other ways that you could get hold of the constructor,
-but only Yummy is usable as a type name.
- Public extension types
- An extension type can be declared public , in which case a .h
-file is generated containing declarations for its object struct and type
-object. By including the .h file in external C code that you write,
-that code can access the attributes of the extension type.
- Name specification clause
- The part of the class declaration in square brackets is a special feature
- only available for extern or public extension types. The full
-form of this clause is
-[object object_struct_name , type type_object_name ]
- where object_struct_name is the name to assume for the type's C
-struct, and type_object_name is the name to assume for the type's
-statically declared type object. (The object and type clauses can be written
-in either order.)
-If the extension type declaration is inside a cdef extern from
-block, the object clause is required, because Cython must be able to
-generate code that is compatible with the declarations in the header file.
-Otherwise, for extern extension types, the object clause is
-optional.
- For public extension types, the object and type clauses
-are both required, because Cython must be able to generate code that is compatible
-with external C code.
-
-
- Back to the Language Overview
-
-
-
\ No newline at end of file
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/FAQ.html cython-0.20.1+1~202203241016-9537/Doc/FAQ.html
--- cython-0.20.1+1~201611251650-6686/Doc/FAQ.html 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/FAQ.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,77 +0,0 @@
-
-
-
- FAQ.html
-
-
- Cython FAQ
-
-
- Contents
-
- How do I call Python/C API routines?
- Declare them as C functions inside a cdef extern from block.
-Use the type name object for any parameters and return types which
-are Python object references. Don't use the word const anywhere.
-Here is an example which defines and uses the PyString_FromStringAndSize routine:
-cdef extern from "Python.h":
- object PyString_FromStringAndSize(char *, int) cdef char buf[42]
- my_string = PyString_FromStringAndSize(buf, 42)
-
- How do I convert a C string containing null
-bytes to a Python string?
- Put in a declaration for the PyString_FromStringAndSize API routine
- and use that. See How do I call Python/C API
- routines? How do I access the data inside a Numeric
- array object?
- Use a cdef extern from block to include the Numeric header file
- and declare the array object as an external extension type. The following
- code illustrates how to do this:
-cdef extern from "Numeric/arrayobject.h": struct PyArray_Descr:
- int type_num, elsize
- char type
- ctypedef class Numeric.ArrayType [object PyArrayObject] :
- cdef char *data
- cdef int nd
- cdef int *dimensions,
-*strides
- cdef object base
-
- cdef PyArray_Descr *descr
- cdef int flags
-
-
-For more information about external extension types, see the "External Extension Types"
-section of the "Extension Types" documentation
-page.
-
- Cython says my extension type object has no attribute
-'rhubarb', but I know it does. What gives?
-You're probably trying to access it through a reference which Cython thinks
-is a generic Python object. You need to tell Cython that it's a reference
-to your extension type by means of a declaration,
-for example,
-cdef class Vegetables:
- cdef int rhubarb
-
- ...
- cdef Vegetables veg
- veg.rhubarb = 42
-
-Also see the "Attributes"
-section of the "Extension
-Types" documentation page.
- Python says my extension type has no method called 'quack', but I know it does. What gives?
-You may have declared the method using cdef instead of def . Only functions and methods declared with def are callable from Python code.
----
-
\ No newline at end of file
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/index.html cython-0.20.1+1~202203241016-9537/Doc/index.html
--- cython-0.20.1+1~201611251650-6686/Doc/index.html 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/index.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,74 +0,0 @@
-
-
-
-
-
- Cython - Front Page
-
-
-
-
-
-Cython
-
-A
-smooth blend of the finest Python
-with the unsurpassed power
-of raw C.
-
-
-
-Welcome to Cython, a language for writing Python
-extension modules. Cython makes creating an extension module is almost as
-easy as creating a Python module! To find out more, consult one of the
-edifying documents below.
-
-
-Documentation
-
-
-
-
-Read this to find out what Cython is all about
-and what it can do for you.
-
-
-
-A description of all the features of the Cython
-language. This is the closest thing to a reference manual in existence
-yet.
-
-
-
-Want to know how to do something in Cython? Check
-here first.
-
-
-
-Other Resources
-
-
-
-
-This tutorial-style presentation will take you
-through the steps of creating some Cython modules to wrap existing C libraries.
-Contributed by Michael JasonSmith .
-
-
-
-If you have a question that's not answered by
-anything here, you're not sure about something, or you have a bug to report
-or a suggestion to make, or anything at all to say about Cython, feel free
-to email me: greg@cosc.canterbury.ac.nz
-
-
-
-
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/overview.html cython-0.20.1+1~202203241016-9537/Doc/overview.html
--- cython-0.20.1+1~201611251650-6686/Doc/overview.html 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/overview.html 1970-01-01 00:00:00.000000000 +0000
@@ -1,960 +0,0 @@
-
-
-
-
-
-
-
- Cython Language Overview
-
-
-
- Overview of the Cython Language
-
- This document informally describes the extensions to the Python language
- made by Cython. Some day there will be a reference manual covering everything
- in more detail.
-
-
- Contents
-
-
-
-
-
- Basics
-
-
- This section describes the basic features of the Cython language. The facilities
- covered in this section allow you to create Python-callable functions that
- manipulate C data structures and convert between Python and C data types.
- Later sections will cover facilities for wrapping external C code , creating new Python types and cooperation between Cython modules .
- Python functions vs. C functions
-
- There are two kinds of function definition in Cython:
-Python functions are defined using the def statement, as
- in Python. They take Python objects as parameters and return Python objects.
-
-
-
-C functions are defined using the new cdef statement. They
- take either Python objects or C values as parameters, and can return either
- Python objects or C values.
-
-
-Within a Cython module, Python functions and C functions can call each other
-freely, but only Python functions can be called from outside the module by
-interpreted Python code. So, any functions that you want to "export" from
- your Cython module must be declared as Python functions using def .
-
-
-Parameters of either type of function can be declared to have C data types,
- using normal C declaration syntax. For example,
-
-
- def spam(int i, char *s): ...
- cdef int eggs(unsigned long l, float f): ...
-
-
- When a parameter of a Python function is declared to have a C data type,
- it is passed in as a Python object and automatically converted to a C value,
- if possible. Automatic conversion is currently only possible for numeric
-types and string types; attempting to use any other type for the parameter
-of a Python function will result in a compile-time error.
-C functions, on the other hand, can have parameters of any type, since
- they're passed in directly using a normal C function call.
-
-
- Python objects as parameters and return values
-
- If no type is specified for a parameter or return value, it is assumed
- to be a Python object. (Note that this is different from the C convention,
- where it would default to int .) For example, the following defines
- a C function that takes two Python objects as parameters and returns a Python
- object:
- cdef spamobjs(x, y): ...
-
-
- Reference counting for these objects is performed automatically according
- to the standard Python/C API rules (i.e. borrowed references are taken as
- parameters and a new reference is returned).
-The name object can also be used to explicitly declare something
- as a Python object. This can be useful if the name being declared would otherwise
-be taken as the name of a type, for example,
-
-
- cdef ftang(object int): ...
-
-
- declares a parameter called int which is a Python object. You
-can also use object as the explicit return type of a function, e.g.
-
- cdef object ftang(object int): ...
-
-
- In the interests of clarity, it is probably a good idea to always be explicit
- about object parameters in C functions.
- C variable and type definitions
-
- The cdef statement is also used to declare C variables, either
-local or module-level:
- cdef int i, j, k cdef float f, g[42], *h
-
-
- and C struct, union or enum types:
- cdef struct Grail: int age float volume
- cdef union Food: char *spam float *eggs
- cdef enum CheeseType: cheddar, edam, camembert
- cdef enum CheeseState: hard = 1 soft = 2 runny = 3
-
-
- There is currently no special syntax for defining a constant, but you
-can use an anonymous enum declaration for this purpose, for example,
-cdef enum:
- tons_of_spam = 3
-
- Note that the words struct , union and enum are used only when defining a type, not when referring to it. For example, to declare a variable pointing
- to a Grail you would write
- cdef Grail *gp
-
-
- and not
- cdef struct Grail *gp # WRONG
-
-
- There is also a ctypedef statement for giving names to types, e.g.
-
- ctypedef unsigned long ULong
- ctypedef int *IntPtr
-
- Automatic type conversions
-
-In most situations, automatic conversions will be performed for the
-basic numeric and string types when a Python object is used in a
-context requiring a C value, or vice versa. The following table
-summarises the conversion possibilities.
-
-
-
-
-
-
-
- C types
-
- From Python types
-
- To Python types
-
-
-
- [unsigned] char
-[unsigned] short
- int, long
- int, long
-
- int
-
-
-
-
-
-
- unsigned int
-unsigned long
- [unsigned] long long
-
-
- int, long
-
-
-
- long
-
-
-
-
-
-
-
-
-
-
- float, double, long double
-
- int, long, float
-
- float
-
-
-
- char *
-
- str
-
- str
-
-
-
-
-
-
-
- Caveats when using a Python string in a C context
-
-You need to be careful when using a Python string in a context expecting a char * .
-In this situation, a pointer to the contents of the Python string is
-used, which is only valid as long as the Python string exists. So you
-need to make sure that a reference to the original Python string is
-held for as long as the C string is needed. If you can't guarantee that
-the Python string will live long enough, you will need to copy the C
-string.
-
-
-
-Cython detects and prevents some mistakes of this kind. For instance, if you attempt something like
-
-cdef char *s s = pystring1 + pystring2
-
-then Cython will produce the error message "Obtaining char * from temporary Python value ".
-The reason is that concatenating the two Python strings produces a new
-Python string object that is referenced only by a temporary internal
-variable that Cython generates. As soon as the statement has finished,
-the temporary variable will be decrefed and the Python string
-deallocated, leaving s dangling. Since this code could not possibly work, Cython refuses to compile it.
-
-
-
-The solution is to assign the result of the concatenation to a Python variable, and then obtain the char * from that, i.e.
-
-cdef char *s p = pystring1 + pystring2 s = p
-
-It is then your responsibility to hold the reference p for as long as necessary.
-
-
-
-Keep in mind that the rules used to detect such errors are only
-heuristics. Sometimes Cython will complain unnecessarily, and sometimes
-it will fail to detect a problem that exists. Ultimately, you need to
-understand the issue and be careful what you do.
-
-
-
-
-
-
- Scope rules
-
- Cython determines whether a variable belongs to a local scope, the module
- scope, or the built-in scope completely statically. As with Python,
- assigning to a variable which is not otherwise declared implicitly declares
- it to be a Python variable residing in the scope where it is assigned. Unlike
- Python, however, a name which is referred to but not declared or assigned
- is assumed to reside in the builtin scope, not the module scope.
-Names added to the module dictionary at run time will not shadow such names.
-
-You can use a global statement at the module level to explicitly
- declare a name to be a module-level name when there would otherwise not be
-any indication of this, for example,
-
-
-global __name__
- print __name__
-
- Without the global statement, the above would print the name of
-the builtins module.
-
-
-
- Note: A consequence of these rules is that the module-level scope behaves
- the same way as a Python local scope if you refer to a variable before assigning
- to it. In particular, tricks such as the following will not work
-in Cython:
-
-
- try: x = True except NameError: True = 1
-
-
- because, due to the assignment, the True will always be looked up in the
- module-level scope. You would have to do something like this instead:
-
-
- import __builtin__ try: True = __builtin__.True except AttributeError: True = 1
-
-
-
-
- Statements and expressions
-
- Control structures and expressions follow Python syntax for the most part.
- When applied to Python objects, they have the same semantics as in Python
- (unless otherwise noted). Most of the Python operators can also be applied
- to C values, with the obvious semantics.
-If Python objects and C values are mixed in an expression, conversions
- are performed automatically between Python objects and C numeric or string
- types.
-
-
-Reference counts are maintained automatically for all Python objects, and
-all Python operations are automatically checked for errors, with appropriate
- action taken.
-
-
- Differences between C and Cython
-expressions
-There
-are some differences in syntax and semantics between C expressions and
-Cython expressions, particularly in the area of C constructs which have
-no direct equivalent in Python.
-
-
-An integer literal without an L suffix is treated as a C constant, and will be truncated to whatever size your C compiler thinks appropriate. With an L suffix, it will be converted to Python long integer (even if it would be small enough to fit into a C int).
-
-
- There is no -> operator in Cython. Instead of p->x ,
- use p.x
-
- There is no * operator in Cython. Instead of
- *p , use p[0]
-
- There is an & operator, with the same semantics
- as in C.
-
- The null C pointer is called NULL , not 0 (and
- NULL is a reserved word).
-
- Character literals are written with a c prefix, for
-example:
- c'X'
-
- Type casts are written <type>value , for example:
- cdef char *p, float *q p = <char*>q
-
- Warning : Don't attempt to use a typecast to convert between
-Python and C data types -- it won't do the right thing. Leave Cython to perform
-the conversion automatically.
-
-
-
- Integer for-loops
-
- You should be aware that a for-loop such as
-for i in range(n):
- ...
-
- won't be very fast, even if i and n are declared as
-C integers, because range is a Python function. For iterating over
-ranges of integers, Cython has another form of for-loop:
-for i from 0 <= i < n:
- ...
-
- If the loop variable and the lower and upper bounds are all C integers,
-this form of loop will be much faster, because Cython will translate it into
-pure C code.
-Some things to note about the for-from loop:
-
-
-
-
- The target expression must be a variable name.
- The name between the lower and upper bounds must be the same as
-the target name.
- The direction of iteration is determined by the relations. If they
- are both from the set {< , <= } then it is upwards;
- if they are both from the set {> , >= } then it is
-downwards. (Any other combination is disallowed.)
-
-
-
- Like other Python looping statements, break and continue may be used in the body, and the loop may have an else clause.
-
-
-
-
- Error return values
-
- If you don't do anything special, a function declared with cdef that does not return a Python object has no way of reporting Python exceptions
- to its caller. If an exception is detected in such a function, a warning
-message is printed and the exception is ignored.
-If you want a C function that does not return a Python object to be able
- to propagate exceptions to its caller, you need to declare an exception
- value for it. Here is an example:
-
-
-cdef int spam() except -1:
- ...
-
- With this declaration, whenever an exception occurs inside spam ,
- it will immediately return with the value -1 . Furthermore, whenever
- a call to spam returns -1 , an exception will be assumed
- to have occurred and will be propagated.
-When you declare an exception value for a function, you should never explicitly
- return that value. If all possible return values are legal and you can't
-reserve one entirely for signalling errors, you can use an alternative form
-of exception value declaration:
-
-
-cdef int spam() except? -1:
- ...
-
- The "?" indicates that the value -1 only indicates a possible error. In this case, Cython generates a call to PyErr_Occurred if the
-exception value is returned, to make sure it really is an error.
-There is also a third form of exception value declaration:
-
-
-cdef int spam() except *:
- ...
-
- This form causes Cython to generate a call to PyErr_Occurred after
- every call to spam, regardless of what value it returns. If you have
- a function returning void that needs to propagate errors, you will
- have to use this form, since there isn't any return value to test.
-Some things to note:
-
-
-
-
- Currently, exception values can only declared for functions returning
- an integer, float or pointer type, and the value must be a literal ,
- not an expression (although it can be negative). The only possible pointer
- exception value is NULL . Void functions can only use the except
- * form.
-
- The exception value specification is part of the signature
-of the function. If you're passing a pointer to a function as a parameter
-or assigning it to a variable, the declared type of the parameter or variable
- must have the same exception value specification (or lack thereof). Here
-is an example of a pointer-to-function declaration with an exception value:
- int (*grail)(int, char *) except -1
-
- You don't need to (and shouldn't) declare exception values for functions
- which return Python objects. Remember that a function with no declared return
- type implicitly returns a Python object.
-
-
-
-
- Checking return values of non-Cython
- functions
-
- It's important to understand that the except clause does not cause an error to be raised when the specified value is returned. For
-example, you can't write something like
- cdef extern FILE *fopen(char *filename, char *mode) except NULL # WRONG!
-
-
- and expect an exception to be automatically raised if a call to fopen
-returns NULL. The except clause doesn't work that way; its only purpose
-is for propagating exceptions that have already been raised, either
-by a Cython function or a C function that calls Python/C API routines. To
-get an exception from a non-Python-aware function such as fopen, you will
-have to check the return value and raise it yourself, for example,
- cdef FILE *p p = fopen("spam.txt", "r") if p == NULL: raise SpamError("Couldn't open the spam file")
-
-
-
-
-
-
- The include statement
-
- For convenience, a large Cython module can be split up into a number of
-files which are put together using the include statement, for example
-
- include "spamstuff.pxi"
-
-
- The contents of the named file are textually included at that point. The
- included file can contain any complete top-level Cython statements, including
- other include statements. The include statement itself can
-only appear at the top level of a file.
-The include statement can also be used in conjunction with public declarations to make C functions and
- variables defined in one Cython module accessible to another. However, note
- that some of these uses have been superseded by the facilities described
-in Sharing Declarations Between Cython Modules ,
-and it is expected that use of the include statement for this purpose
-will be phased out altogether in future versions.
-
-
- Interfacing with External
- C Code
-
-
- One of the main uses of Cython is wrapping existing libraries of C code.
-This is achieved by using external declarations to declare the C functions and variables from the library that you want to
- use.
-You can also use public declarations to make
- C functions and variables defined in a Cython module available to external
- C code. The need for this is expected to be less frequent, but you might
-want to do it, for example, if you are embedding Python in another application
- as a scripting language. Just as a Cython module can be used as a bridge to
-allow Python code to call C code, it can also be used to allow C code to
-call Python code.
-
-
- External declarations
-
- By default, C functions and variables declared at the module level are
-local to the module (i.e. they have the C static storage class). They
-can also be declared extern to specify that they are defined elsewhere,
- for example:
- cdef extern int spam_counter
- cdef extern void order_spam(int tons)
-
-
-
-
-
-
- Referencing C header files
-
- When you use an extern definition on its own as in the examples above,
-Cython includes a declaration for it in the generated C file. This can cause
-problems if the declaration doesn't exactly match the declaration that will
-be seen by other C code. If you're wrapping an existing C library, for example,
-it's important that the generated C code is compiled with exactly the same
-declarations as the rest of the library.
-To achieve this, you can tell Cython that the declarations are to be found
- in a C header file, like this:
-
-
- cdef extern from "spam.h":
- int spam_counter
- void order_spam(int tons)
-
-
- The cdef extern from clause does three things:
-
-
- It directs Cython to place a #include statement for the named
- header file in the generated C code.
-
- It prevents Cython from generating any C code for the declarations
- found in the associated block.
-
- It treats all declarations within the block as though they
-started with cdef extern .
-
-
-
- It's important to understand that Cython does not itself read the
-C header file, so you still need to provide Cython versions of any declarations
- from it that you use. However, the Cython declarations don't always have to
-exactly match the C ones, and in some cases they shouldn't or can't. In particular:
-
-
-
- Don't use const . Cython doesn't know anything about const,
-so just leave it out. Most of the time this shouldn't cause any problem,
-although on rare occasions you might have to use a cast. 1
-
- Leave out any platform-specific extensions to C declarations
- such as __declspec() .
-
- If the header file declares a big struct and you only want
-to use a few members, you only need to declare the members you're interested
-in. Leaving the rest out doesn't do any harm, because the C compiler will
-use the full definition from the header file.
-
- In some cases, you might not need any of the struct's members, in
-which case you can just put pass in the body of the struct declaration,
-e.g.
-
- cdef extern from "foo.h":
- struct spam:
- pass
-
-Note that you can only do this inside a cdef extern from block; struct
-declarations anywhere else must be non-empty.
-
-
- If the header file uses typedef names such as size_t to refer
-to platform-dependent flavours of numeric types, you will need a corresponding
- ctypedef statement, but you don't need to match the type exactly,
- just use something of the right general kind (int, float, etc). For example,
-
- ctypedef int size_t
-
- will work okay whatever the actual size of a size_t is (provided the header
- file defines it correctly).
- If the header file uses macros to define constants, translate
- them into a dummy enum declaration.
-
- If the header file defines a function using a macro, declare
- it as though it were an ordinary function, with appropriate argument and
-result types.
-
-
-
- A few more tricks and tips:
-
-
- If you want to include a C header because it's needed by another
-header, but don't want to use any declarations from it, put pass in the extern-from block:
-
-
-
-
-
-
-
- cdef extern from "spam.h":
- pass
-
-
-
-
-
-
- If you want to include some external declarations, but don't want
-to specify a header file (because it's included by some other header that
-you've already included) you can put * in place of the header file
-name:
-
-
-
-
- cdef extern from *:
- ...
-
-
-
- Styles of struct, union and enum declaration
-
- There are two main ways that structs, unions and enums can be declared
-in C header files: using a tag name, or using a typedef. There are also some
- variations based on various combinations of these.
-It's important to make the Cython declarations match the style used in the
-header file, so that Cython can emit the right sort of references to the type
-in the code it generates. To make this possible, Cython provides two different
-syntaxes for declaring a struct, union or enum type. The style introduced
-above corresponds to the use of a tag name. To get the other style, you prefix
-the declaration with ctypedef , as illustrated below.
-
-
-The following table shows the various possible styles that can be found
- in a header file, and the corresponding Cython declaration that you should
- put in the cdef exern from block. Struct declarations are used as
-an example; the same applies equally to union and enum declarations.
-
-
-Note that in all the cases below, you refer to the type in Cython code simply
-as Foo , not struct Foo .
-
-
-
-
-
- C code
- Possibilities for corresponding
-Cython code
- Comments
-
-
- 1
- struct Foo {
- ...
- };
- cdef struct Foo:
- ...
- Cython will refer to the type as struct Foo in the generated
- C code.
-
-
- 2
- typedef struct {
- ...
- } Foo;
- ctypedef struct Foo:
- ...
- Cython will refer to the type simply as Foo
-in the generated C code.
-
-
- 3
- typedef struct
-foo {
- ...
- } Foo;
- cdef struct foo:
- ...
- ctypedef foo Foo #optional
- If the C header uses both a tag and a typedef
- with different names, you can use either form of declaration in Cython
- (although if you need to forward reference the type, you'll have to use
-the first form).
-
-
- ctypedef struct Foo:
- ...
-
-
- 4
- typedef struct Foo {
- ...
- } Foo;
- cdef struct Foo:
- ...
- If the header uses the same name for the tag and the typedef,
- you won't be able to include a ctypedef for it -- but then, it's not
-necessary.
-
-
-
-
-
- Accessing Python/C API routines
-
- One particular use of the cdef extern from statement is for gaining
- access to routines in the Python/C API. For example,
- cdef extern from "Python.h":
- object PyString_FromStringAndSize(char *s, int len)
-
-
- will allow you to create Python strings containing null bytes.
-
-
-
-
- Resolving naming conflicts - C name specifications
-
- Each Cython module has a single module-level namespace for both Python
-and C names. This can be inconvenient if you want to wrap some external
-C functions and provide the Python user with Python functions of the same
-names.
-Cython 0.8 provides a couple of different ways of solving this problem.
- The best way, especially if you have many C functions to wrap, is probably
- to put the extern C function declarations into a different namespace using
- the facilities described in the section on sharing
- declarations between Cython modules .
-
-
-The other way is to use a c name specification to give different
- Cython and C names to the C function. Suppose, for example, that you want
-to wrap an external function called eject_tomato . If you declare
-it as
-
-
- cdef extern void c_eject_tomato "eject_tomato" (float speed)
-
-
- then its name inside the Cython module will be c_eject_tomato ,
-whereas its name in C will be eject_tomato . You can then wrap it
-with
- def eject_tomato(speed): c_eject_tomato(speed)
-
-
- so that users of your module can refer to it as eject_tomato .
-
-Another use for this feature is referring to external names that happen
- to be Cython keywords. For example, if you want to call an external function
- called print , you can rename it to something else in your Cython
-module.
-
-
-As well as functions, C names can be specified for variables, structs,
- unions, enums, struct and union members, and enum values. For example,
-
-
- cdef extern int one "ein", two "zwei" cdef extern float three "drei" cdef struct spam "SPAM": int i "eye"
- cdef enum surprise "inquisition":
- first "alpha"
- second "beta" = 3
-
-
-
- Public Declarations
-
- You can make C variables and functions defined in a Cython module accessible
- to external C code (or another Cython module) using the public keyword, as follows:
-cdef public int spam # public variable declaration cdef public void grail(int num_nuns): # public function declaration
- ...
-
-
- If there are any public declarations in a Cython module, a .h file is generated containing equivalent C declarations for inclusion in other
- C code.
-Cython also generates a .pxi file containing Cython versions of the
- declarations for inclusion in another Cython module using the include statement. If you use this, you
- will need to arrange for the module using the declarations to be linked
-against the module defining them, and for both modules to be available to
-the dynamic linker at run time. I haven't tested this, so I can't say how
-well it will work on the various platforms.
-
-
-NOTE: If all you want to export is an extension type, there is
- now a better way -- see Sharing Declarations Between
- Cython Modules .
-
-
- Extension Types
-
-
- One of the most powerful features of Cython is the ability to easily create
- new built-in Python types, called extension types . This is a major
- topic in itself, so there is a separate
- page devoted to it.
- Sharing Declarations Between Cython Modules
-
-
- Cython 0.8 introduces a substantial new set of facilities allowing a Cython
- module to easily import and use C declarations and extension types from another
-Cython module. You can now create a set of co-operating Cython modules just
-as easily as you can create a set of co-operating Python modules. There is
-a separate page devoted to this topic.
- Limitations
-
-
-
- Unsupported Python features
-
- Cython is not quite a full superset of Python. The following restrictions
- apply:
- Function definitions (whether using def or cdef )
- cannot be nested within other function definitions.
-
- Class definitions can only appear at the top level of a module,
- not inside a function.
-
- The import * form of import is not allowed anywhere
- (other forms of the import statement are fine, though).
-
- Generators cannot be defined in Cython.
-
-
- The globals() and locals() functions cannot be
-used.
-
-
- The above restrictions will most likely remain, since removing them would
- be difficult and they're not really needed for Cython's intended applications.
-
-There are also some temporary limitations, which may eventually be lifted, including:
-
-
-
- Class and function definitions cannot be placed inside
-control structures.
-
- In-place arithmetic operators (+=, etc) are not yet supported.
-
- List comprehensions are not yet supported.
-
- There is no support for Unicode.
-
- Special methods of extension types cannot have functioning
-docstrings.
-
-
- The use of string literals as comments is not recommended at present,
- because Cython doesn't optimize them away, and won't even accept them in
-places where executable statements are not allowed.
-
-
- Semantic differences between Python
- and Cython
-
-
- Behaviour of class scopes
-
- In Python, referring to a method of a class inside the class definition,
- i.e. while the class is being defined, yields a plain function object, but
- in Cython it yields an unbound method2 . A consequence of this is that the
-usual idiom for using the classmethod and staticmethod functions, e.g.
- class Spam:
- def method(cls): ...
- method = classmethod(method)
-
-
- will not work in Cython. This can be worked around by defining the function
- outside the class, and then assigning the result of classmethod or
- staticmethod inside the class, i.e.
- def Spam_method(cls): ...
- class Spam:
- method = classmethod(Spam_method)
-
-
-
- Footnotes
-
- 1. A problem with const could arise if you have
-something like
- cdef extern from "grail.h": char *nun
-
-
- where grail.h actually contains
- extern const char *nun;
-
-
- and you do
- cdef void languissement(char *s): #something that doesn't change s
- ...
- languissement(nun)
-
-
- which will cause the C compiler to complain. You can work around it by
-casting away the constness:
- languissement(<char *>nun)
-
-
-
- 2. The reason for the different behaviour
-of class scopes is that Cython-defined Python functions are PyCFunction objects,
-not PyFunction objects, and are not recognised by the machinery that creates
-a bound or unbound method when a function is extracted from a class. To get
-around this, Cython wraps each method in an unbound method object itself before
-storing it in the class's dictionary.
-
-
-
-
-
-
\ No newline at end of file
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/primes.c cython-0.20.1+1~202203241016-9537/Doc/primes.c
--- cython-0.20.1+1~201611251650-6686/Doc/primes.c 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/primes.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,142 +0,0 @@
-#include "Python.h"
-
-
-static PyObject *__Pyx_UnpackItem(PyObject *, int);
-static int __Pyx_EndUnpack(PyObject *, int);
-static int __Pyx_PrintItem(PyObject *);
-static int __Pyx_PrintNewline(void);
-static void __Pyx_ReRaise(void);
-static void __Pyx_RaiseWithTraceback(PyObject *, PyObject *, PyObject *);
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list);
-static PyObject *__Pyx_GetExcValue(void);
-static PyObject *__Pyx_GetName(PyObject *dict, char *name);
-
-static PyObject *__pyx_m;
-static PyObject *__pyx_d;
-static PyObject *__pyx_b;
-
-
-PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args); /*proto*/
-PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args) {
- int __pyx_v_kmax;
- int __pyx_v_n;
- int __pyx_v_k;
- int __pyx_v_i;
- int (__pyx_v_p[1000]);
- PyObject *__pyx_v_result;
- PyObject *__pyx_r;
- PyObject *__pyx_1 = 0;
- int __pyx_2;
- int __pyx_3;
- int __pyx_4;
- PyObject *__pyx_5 = 0;
- PyObject *__pyx_6 = 0;
- if (!PyArg_ParseTuple(__pyx_args, "i", &__pyx_v_kmax)) return 0;
- __pyx_v_result = Py_None; Py_INCREF(__pyx_v_result);
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":2 */
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":3 */
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":4 */
- __pyx_1 = PyList_New(0); if (!__pyx_1) goto __pyx_L1;
- Py_DECREF(__pyx_v_result);
- __pyx_v_result = __pyx_1;
- __pyx_1 = 0;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":5 */
- __pyx_2 = (__pyx_v_kmax > 1000);
- if (__pyx_2) {
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":6 */
- __pyx_v_kmax = 1000;
- goto __pyx_L2;
- }
- __pyx_L2:;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":7 */
- __pyx_v_k = 0;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":8 */
- __pyx_v_n = 2;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":9 */
- while (1) {
- __pyx_L3:;
- __pyx_2 = (__pyx_v_k < __pyx_v_kmax);
- if (!__pyx_2) break;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":10 */
- __pyx_v_i = 0;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":11 */
- while (1) {
- __pyx_L5:;
- if (__pyx_3 = (__pyx_v_i < __pyx_v_k)) {
- __pyx_3 = ((__pyx_v_n % (__pyx_v_p[__pyx_v_i])) != 0);
- }
- if (!__pyx_3) break;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":12 */
- __pyx_v_i = (__pyx_v_i + 1);
- }
- __pyx_L6:;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":13 */
- __pyx_4 = (__pyx_v_i == __pyx_v_k);
- if (__pyx_4) {
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":14 */
- (__pyx_v_p[__pyx_v_k]) = __pyx_v_n;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":15 */
- __pyx_v_k = (__pyx_v_k + 1);
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":16 */
- __pyx_1 = PyObject_GetAttrString(__pyx_v_result, "append"); if (!__pyx_1) goto __pyx_L1;
- __pyx_5 = PyInt_FromLong(__pyx_v_n); if (!__pyx_5) goto __pyx_L1;
- __pyx_6 = PyTuple_New(1); if (!__pyx_6) goto __pyx_L1;
- PyTuple_SET_ITEM(__pyx_6, 0, __pyx_5);
- __pyx_5 = 0;
- __pyx_5 = PyObject_CallObject(__pyx_1, __pyx_6); if (!__pyx_5) goto __pyx_L1;
- Py_DECREF(__pyx_6); __pyx_6 = 0;
- Py_DECREF(__pyx_5); __pyx_5 = 0;
- goto __pyx_L7;
- }
- __pyx_L7:;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":17 */
- __pyx_v_n = (__pyx_v_n + 1);
- }
- __pyx_L4:;
-
- /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":18 */
- Py_INCREF(__pyx_v_result);
- __pyx_r = __pyx_v_result;
- goto __pyx_L0;
-
- __pyx_r = Py_None; Py_INCREF(__pyx_r);
- goto __pyx_L0;
- __pyx_L1:;
- Py_XDECREF(__pyx_1);
- Py_XDECREF(__pyx_5);
- Py_XDECREF(__pyx_6);
- __pyx_r = 0;
- __pyx_L0:;
- Py_DECREF(__pyx_v_result);
- return __pyx_r;
-}
-
-static struct PyMethodDef __pyx_methods[] = {
- {"primes", (PyCFunction)__pyx_f_primes, METH_VARARGS, 0},
- {0, 0, 0, 0}
-};
-
-void initprimes(void); /*proto*/
-void initprimes(void) {
- __pyx_m = Py_InitModule4("primes", __pyx_methods, 0, 0, PYTHON_API_VERSION);
- __pyx_d = PyModule_GetDict(__pyx_m);
- __pyx_b = PyImport_AddModule("__builtin__");
- PyDict_SetItemString(__pyx_d, "__builtins__", __pyx_b);
-}
-/* Runtime support code */
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/s5/cython-ep2008.txt cython-0.20.1+1~202203241016-9537/Doc/s5/cython-ep2008.txt
--- cython-0.20.1+1~201611251650-6686/Doc/s5/cython-ep2008.txt 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/s5/cython-ep2008.txt 2022-03-24 10:16:46.000000000 +0000
@@ -51,7 +51,7 @@
* an Open-Source project
- * http://cython.org
+ * https://cython.org/
* a Python compiler (almost)
@@ -115,7 +115,7 @@
* many, *many* others - see
- * http://cython.org/
+ * https://cython.org/
* the mailing list archives of Cython and Pyrex
@@ -398,4 +398,4 @@
\... use it, and join the project!
- http://cython.org/
+ https://cython.org/
Binary files /tmp/tmp2d4wmwme/ShV34fQhQG/cython-0.20.1+1~201611251650-6686/Doc/s5/ui/default/cython-logo64.png and /tmp/tmp2d4wmwme/GdWEr1gunb/cython-0.20.1+1~202203241016-9537/Doc/s5/ui/default/cython-logo64.png differ
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/s5/ui/default/framing.css cython-0.20.1+1~202203241016-9537/Doc/s5/ui/default/framing.css
--- cython-0.20.1+1~201611251650-6686/Doc/s5/ui/default/framing.css 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/s5/ui/default/framing.css 2022-03-24 10:16:46.000000000 +0000
@@ -1,6 +1,6 @@
/* The following styles size, place, and layer the slide components.
Edit these if you want to change the overall slide layout.
- The commented lines can be uncommented (and modified, if necessary)
+ The commented lines can be uncommented (and modified, if necessary)
to help you with the rearrangement process. */
/* target = 1024x768 */
diff -Nru cython-0.20.1+1~201611251650-6686/Doc/s5/ui/default/iepngfix.htc cython-0.20.1+1~202203241016-9537/Doc/s5/ui/default/iepngfix.htc
--- cython-0.20.1+1~201611251650-6686/Doc/s5/ui/default/iepngfix.htc 2016-11-25 16:50:54.000000000 +0000
+++ cython-0.20.1+1~202203241016-9537/Doc/s5/ui/default/iepngfix.htc 2022-03-24 10:16:46.000000000 +0000
@@ -3,7 +3,7 @@
\n",
+ "\n",
+ "\n",
+ "Generated by Cython 0.25.2
\n",
+ "\n",
+ " Yellow lines hint at Python interaction. \n",
+ " Click on a line that starts with a \"+
\" to see the C code that Cython generated for it.\n",
+ "
\n",
+ " 1 : \n",
+ "
+2 : cdef int a = 0 \n",
+ "
__pyx_v_46_cython_magic_6ba45f17d130910db2606828f4326b2d_a = 0;\n",
+ " +3 : for i in range ( 10 ): \n",
+ "
__pyx_t_1 = __Pyx_PyObject_Call (__pyx_builtin_range, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_1);\n",
+ " if (likely(PyList_CheckExact (__pyx_t_1)) || PyTuple_CheckExact (__pyx_t_1)) {\n",
+ " __pyx_t_2 = __pyx_t_1; __Pyx_INCREF (__pyx_t_2); __pyx_t_3 = 0;\n",
+ " __pyx_t_4 = NULL;\n",
+ " } else {\n",
+ " __pyx_t_3 = -1; __pyx_t_2 = PyObject_GetIter (__pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_2);\n",
+ " __pyx_t_4 = Py_TYPE(__pyx_t_2)->tp_iternext; if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " }\n",
+ " __Pyx_DECREF (__pyx_t_1); __pyx_t_1 = 0;\n",
+ " for (;;) {\n",
+ " if (likely(!__pyx_t_4)) {\n",
+ " if (likely(PyList_CheckExact (__pyx_t_2))) {\n",
+ " if (__pyx_t_3 >= PyList_GET_SIZE (__pyx_t_2)) break;\n",
+ " #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS\n",
+ " __pyx_t_1 = PyList_GET_ITEM (__pyx_t_2, __pyx_t_3); __Pyx_INCREF (__pyx_t_1); __pyx_t_3++; if (unlikely(0 < 0)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " #else\n",
+ " __pyx_t_1 = PySequence_ITEM (__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_1);\n",
+ " #endif\n",
+ " } else {\n",
+ " if (__pyx_t_3 >= PyTuple_GET_SIZE (__pyx_t_2)) break;\n",
+ " #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS\n",
+ " __pyx_t_1 = PyTuple_GET_ITEM (__pyx_t_2, __pyx_t_3); __Pyx_INCREF (__pyx_t_1); __pyx_t_3++; if (unlikely(0 < 0)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " #else\n",
+ " __pyx_t_1 = PySequence_ITEM (__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_1);\n",
+ " #endif\n",
+ " }\n",
+ " } else {\n",
+ " __pyx_t_1 = __pyx_t_4(__pyx_t_2);\n",
+ " if (unlikely(!__pyx_t_1)) {\n",
+ " PyObject* exc_type = PyErr_Occurred ();\n",
+ " if (exc_type) {\n",
+ " if (likely(exc_type == PyExc_StopIteration || PyErr_GivenExceptionMatches (exc_type, PyExc_StopIteration))) PyErr_Clear ();\n",
+ " else __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " }\n",
+ " break;\n",
+ " }\n",
+ " __Pyx_GOTREF (__pyx_t_1);\n",
+ " }\n",
+ " if (PyDict_SetItem (__pyx_d, __pyx_n_s_i, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+ " __Pyx_DECREF (__pyx_t_1); __pyx_t_1 = 0;\n",
+ "/* … */\n",
+ " }\n",
+ " __Pyx_DECREF (__pyx_t_2); __pyx_t_2 = 0;\n",
+ " +4 : a += i \n",
+ "
__pyx_t_1 = __Pyx_PyInt_From_int (__pyx_v_46_cython_magic_6ba45f17d130910db2606828f4326b2d_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 4, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_1);\n",
+ " __pyx_t_5 = __Pyx_GetModuleGlobalName (__pyx_n_s_i); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 4, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_5);\n",
+ " __pyx_t_6 = PyNumber_InPlaceAdd (__pyx_t_1, __pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 4, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_6);\n",
+ " __Pyx_DECREF (__pyx_t_1); __pyx_t_1 = 0;\n",
+ " __Pyx_DECREF (__pyx_t_5); __pyx_t_5 = 0;\n",
+ " __pyx_t_7 = __Pyx_PyInt_As_int (__pyx_t_6); if (unlikely((__pyx_t_7 == (int)-1) && PyErr_Occurred ())) __PYX_ERR(0, 4, __pyx_L1_error)\n",
+ " __Pyx_DECREF (__pyx_t_6); __pyx_t_6 = 0;\n",
+ " __pyx_v_46_cython_magic_6ba45f17d130910db2606828f4326b2d_a = __pyx_t_7;\n",
+ " +5 : print ( a ) \n",
+ "
__pyx_t_2 = __Pyx_PyInt_From_int (__pyx_v_46_cython_magic_6ba45f17d130910db2606828f4326b2d_a); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 5, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_2);\n",
+ " __pyx_t_6 = PyTuple_New (1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 5, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_6);\n",
+ " __Pyx_GIVEREF (__pyx_t_2);\n",
+ " PyTuple_SET_ITEM (__pyx_t_6, 0, __pyx_t_2);\n",
+ " __pyx_t_2 = 0;\n",
+ " __pyx_t_2 = __Pyx_PyObject_Call (__pyx_builtin_print, __pyx_t_6, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 5, __pyx_L1_error)\n",
+ " __Pyx_GOTREF (__pyx_t_2);\n",
+ " __Pyx_DECREF (__pyx_t_6); __pyx_t_6 = 0;\n",
+ " __Pyx_DECREF (__pyx_t_2); __pyx_t_2 = 0;\n",
+ "