diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b8db3a8a8d..7421e157168 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: # # FIXME: 'cpp' tests seems to fail due to compilation errors (numpy_pythran_unit) # in all python versions and test failures (builtin_float) in 3.5< - os: [ubuntu-20.04, windows-2019, macos-12] + os: [ubuntu-22.04, windows-2019, macos-13] backend: [c, cpp] python-version: - "3.7" @@ -50,135 +50,136 @@ jobs: - "3.10" - "3.11" - "3.12" - - "3.13-dev" + - "3.13" env: [{}] include: - #- python-version: "3.13-dev" + #- python-version: "3.13" # allowed_failure: true # Ubuntu sub-jobs: # ================ - # graalpy is really slow, so put it first... - - os: ubuntu-20.04 + # graalpy is really slow... + - os: ubuntu-22.04 python-version: graalpy24 backend: c env: { NO_CYTHON_COMPILE: 1 } - # GCC 11 (with broad language standards) - - os: ubuntu-20.04 + allowed_failure: true + # GCC 13 (with broad language standards) + - os: ubuntu-22.04 python-version: "3.9" backend: c - env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c99" } + env: { GCC_VERSION: 13, EXTRA_CFLAGS: "-std=c99" } extra_hash: "-c99" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.10" backend: c - env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c17" } + env: { GCC_VERSION: 13, EXTRA_CFLAGS: "-std=c17" } extra_hash: "-gcc11" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.12" backend: c - env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c17" } + env: { GCC_VERSION: 13, EXTRA_CFLAGS: "-std=c17" } extra_hash: "-gcc11" - - os: ubuntu-20.04 - python-version: "3.12" + - os: ubuntu-22.04 + python-version: "3.13" backend: cpp - env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c++20" } + env: { GCC_VERSION: 13, EXTRA_CFLAGS: "-std=c++20" } extra_hash: "-gcc11" # compile all modules - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.7" backend: c env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.7" backend: cpp env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.11" backend: c env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.11" backend: cpp env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" # Linting - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.9" backend: "c,cpp" env: { TEST_CODE_STYLE: 1, NO_CYTHON_COMPILE: 1 } extra_hash: "-codestyle" # Limited API - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.7" backend: "c,cpp" env: { LIMITED_API: "--limited-api", NO_LIMITED_COMPILE: 1, EXCLUDE: "--no-file" } extra_hash: "-limited_api" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.8" backend: "c,cpp" env: { LIMITED_API: "--limited-api", NO_LIMITED_COMPILE: 1, EXCLUDE: "--no-file" } extra_hash: "-limited_api" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.11" backend: "c,cpp" env: { LIMITED_API: "--limited-api", EXCLUDE: "--no-file" } extra_hash: "-limited_api" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.12" allowed_failure: true backend: "c,cpp" env: { LIMITED_API: "--limited-api", EXCLUDE: "--no-file" } extra_hash: "-limited_api" - - os: ubuntu-20.04 - python-version: "3.13-dev" + - os: ubuntu-22.04 + python-version: "3.13" allowed_failure: true backend: "c,cpp" env: { LIMITED_API: "--limited-api", EXCLUDE: "--no-file" } extra_hash: "-limited_api" # Type specs - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.12" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.9" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.8" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.7" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" # Stackless - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: "3.8" backend: c env: { STACKLESS: true, PY: 3 } extra_hash: "-stackless" # Pypy - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: pypy-3.9 backend: c env: { NO_CYTHON_COMPILE: 1 } - - os: ubuntu-20.04 + - os: ubuntu-22.04 python-version: pypy-3.10 backend: c env: { NO_CYTHON_COMPILE: 1 } # Free-threading - - os: ubuntu-20.04 - python-version: 3.13-freethreading-dev + - os: ubuntu-22.04 + python-version: 3.13-freethreading backend: "c,cpp" env: {} allowed_failure: true @@ -192,8 +193,8 @@ jobs: env: BACKEND: ${{ matrix.backend }} PYTHON_VERSION: ${{ matrix.python-version }} - MACOSX_DEPLOYMENT_TARGET: 11.0 - GCC_VERSION: 8 + MACOSX_DEPLOYMENT_TARGET: "11.0" + GCC_VERSION: 10 USE_CCACHE: 1 CCACHE_SLOPPINESS: "pch_defines,time_macros" CCACHE_COMPRESS: 1 @@ -207,15 +208,15 @@ jobs: - name: Setup python uses: actions/setup-python@v5.0.0 - if: "!endsWith(matrix.python-version, '-freethreading-dev')" + if: "!endsWith(matrix.python-version, '-freethreading')" with: python-version: ${{ matrix.python-version }} - name: Setup python from deadsnakes uses: deadsnakes/action@v3.1.0 - if: "endsWith(matrix.python-version, '-freethreading-dev')" + if: "endsWith(matrix.python-version, '-freethreading')" with: - python-version: 3.13-dev + python-version: "3.13" nogil: true - name: Compilation Cache @@ -246,11 +247,11 @@ jobs: pycoverage: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: BACKEND: c,cpp - OS_NAME: ubuntu-20.04 + OS_NAME: ubuntu-22.04 PYTHON_VERSION: "3.11" steps: @@ -273,11 +274,11 @@ jobs: path: coverage-report-html cycoverage: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: BACKEND: c,cpp - OS_NAME: ubuntu-20.04 + OS_NAME: ubuntu-22.04 PYTHON_VERSION: "3.11" steps: diff --git a/.github/workflows/nightly-wheels.yml b/.github/workflows/nightly-wheels.yml index 65208c43583..2607f00c969 100644 --- a/.github/workflows/nightly-wheels.yml +++ b/.github/workflows/nightly-wheels.yml @@ -59,7 +59,7 @@ jobs: - name: Upload wheels to scientific-python-nightly-wheels if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' - uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # 0.5.0 + uses: scientific-python/upload-nightly-action@82396a2ed4269ba06c6b2988bb4fd568ef3c3d6b # 0.6.1 with: artifacts_path: dist anaconda_nightly_upload_token: ${{ secrets.CYTHON_NIGHTLY_UPLOAD_TOKEN }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 8cbeb71b30d..faae351c6db 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -66,7 +66,7 @@ jobs: - uses: actions/checkout@v4 - name: Install cibuildwheel # Nb. keep cibuildwheel version pin consistent with job below - run: pipx install cibuildwheel==2.20.0 + run: pipx install cibuildwheel==2.21.3 - id: set-matrix run: | MATRIX=$( @@ -112,7 +112,7 @@ jobs: - name: Build wheels # Nb. keep cibuildwheel version pin consistent with generate-matrix job above - uses: pypa/cibuildwheel@v2.20.0 + uses: pypa/cibuildwheel@v2.21.3 with: only: ${{ matrix.only }} # TODO: Cython tests take a long time to complete diff --git a/CHANGES.rst b/CHANGES.rst index 8ee52c84eb7..8c26d15135f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,6 +28,16 @@ Features added * Several issues with the gdb support were resolved. Patches by Kent Slaney. (Github issues :issue:`5955`, :issue:`5948`) +* ``typing.Union[SomeType, None]`` and ``SomeType | None`` are now understood and mean + the same as ``typing.Optional[SomeType]``, allowing ``None`` in type checks. + (Github issue :issue:`6254`) + +* ``cython.const[]`` and ``cython.volatile[]`` are now available as type modifiers in Python code. + (Github issue :issue:`5728`) + +* ``cython.pointer[SomeCType]`` can now be used to define pointer types in Python type annotations. + (Github issue :issue:`5071`) + * Several improvements were made to reduce the size of the resulting extension modules. (Github issue :issue:`4425`) @@ -113,6 +123,9 @@ Bugs fixed * The ``__class__`` cell variable in methods was not always working as in Python. Initial patch by Tom Keefe. (Github issue :issue:`2912`) +* Lambda functions had no code objects. Their signature can now be introspected. + (Github issue :issue:`2983`) + * Subtyping `complex` as extension type could fail. (Github issue :issue:`6346`) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..3a0ea02a481 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +See [docs/CONTRIBUTING.rst](docs/CONTRIBUTING.rst). diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 88d7e2fa040..883eeae6529 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2852,8 +2852,6 @@ def put_release_ensured_gil(self, variable=None): self.putln("__Pyx_PyGILState_Release(%s);" % variable) def put_acquire_freethreading_lock(self): - self.globalstate.use_utility_code( - UtilityCode.load_cached("AccessPyMutexForFreeThreading", "ModuleSetupCode.c")) self.putln("#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING") self.putln(f"PyMutex_Lock(&{Naming.parallel_freethreading_mutex});") self.putln("#endif") @@ -3099,8 +3097,10 @@ def put_trace_return(self, retvalue_cname, pos, return_type=None, nogil=False): extra_arg = "" trace_func = "__Pyx_TraceReturnValue" - if return_type is None or return_type.is_pyobject: + if return_type is None: pass + elif return_type.is_pyobject: + retvalue_cname = return_type.as_pyobject(retvalue_cname) elif return_type.is_void: retvalue_cname = 'Py_None' elif return_type.to_py_function: diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f09f7601a6f..8dc4433a466 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3902,8 +3902,39 @@ def analyse_target_declaration(self, env): pass def analyse_as_type(self, env): + modifier = self.base.as_cython_attribute() + + if modifier is not None and modifier in ('pointer', 'const', 'volatile'): + base_type = self.index.analyse_as_type(env) + if base_type is None: + error(self.base.pos, f"invalid use of '{modifier}', argument is not a type") + return None + if modifier == 'pointer': + # pointer[base_type] + return PyrexTypes.CPtrType(base_type) + + # const[base_type] or volatile[base_type] + is_const = modifier == 'const' + is_volatile = not is_const + if base_type.is_cv_qualified: + if base_type.is_const: + if is_const: + error(self.base.pos, "Duplicate 'const'") + is_const = True + if base_type.is_volatile: + if is_volatile: + error(self.base.pos, "Duplicate 'volatile'") + is_volatile = True + base_type = base_type.cv_base_type + if base_type.is_memoryviewslice: + error(self.base.pos, + f"Cannot declare memory view variable as '{modifier}'. Did you mean '{modifier}[item_type][:]' ?") + return PyrexTypes.c_const_or_volatile_type( + base_type, is_const=is_const, is_volatile=not is_const) + base_type = self.base.analyse_as_type(env) if base_type: + # base_type[...] if base_type.is_cpp_class or base_type.python_type_constructor_name: if self.index.is_sequence_constructor: template_values = self.index.args @@ -9411,6 +9442,9 @@ def generate_evaluation_code(self, code): len(self.key_value_pairs), code.error_goto_if_null(self.result(), self.pos))) self.generate_gotref(code) + struct_scope = None + else: + struct_scope = self.type.scope keys_seen = set() key_type = None @@ -9459,17 +9493,14 @@ def generate_evaluation_code(self, code): if self.exclude_null_values: code.putln('}') else: + member = struct_scope.lookup_here(item.key.value) + assert member is not None, f"struct member {item.key.value} not found, error was not handled during coercion" + key_cname = member.cname + value_cname = item.value.result() if item.value.type.is_array: - code.putln("memcpy(%s.%s, %s, sizeof(%s));" % ( - self.result(), - item.key.value, - item.value.result(), - item.value.result())) + code.putln(f"memcpy({self.result()}.{key_cname}, {value_cname}, sizeof({value_cname}));") else: - code.putln("%s.%s = %s;" % ( - self.result(), - item.key.value, - item.value.result())) + code.putln(f"{self.result()}.{key_cname} = {value_cname};") item.generate_disposal_code(code) item.free_temps(code) @@ -11681,7 +11712,7 @@ def get_type_info_type(self, env): env_module = env_module.outer_scope typeinfo_module = env_module.find_module('libcpp.typeinfo', self.pos) typeinfo_entry = typeinfo_module.lookup('type_info') - return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type)) + return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_or_volatile_type(typeinfo_entry.type, is_const=True)) cpp_message = 'typeid operator' @@ -12213,6 +12244,34 @@ def c_types_okay(self, type1, type2): and (type2.is_int or type2.is_enum) +class BitwiseOrNode(IntBinopNode): + # '|' operator. + + def analyse_pytyping_modifiers(self, env): + if self.operand1.is_none or self.operand2.is_none: + return ['typing.Optional'] + + def _analyse_bitwise_or_none(self, env, operand_node): + """Analyse annotations in form `[...] | None` and `None | [...]`""" + ttype = operand_node.analyse_as_type(env) + if not ttype: + return None + if not ttype.can_be_optional(): + # If ttype cannot be optional we need to return an equivalent Python type allowing None. + # If it cannot be mapped to a Python type, we must error out. + if ttype.equivalent_type and not operand_node.as_cython_attribute(): + return ttype.equivalent_type + else: + error(operand_node.pos, f"'[...] | None' cannot be applied to type {ttype}") + return ttype + + def analyse_as_type(self, env): + if self.operand1.is_none: + return self._analyse_bitwise_or_none(env, self.operand2) + elif self.operand2.is_none: + return self._analyse_bitwise_or_none(env, self.operand1) + + class AddNode(NumBinopNode): # '+' operator. @@ -13948,7 +14007,7 @@ def annotate(self, code): binop_node_classes = { "or": BoolBinopNode, "and": BoolBinopNode, - "|": IntBinopNode, + "|": BitwiseOrNode, "^": IntBinopNode, "&": IntBinopNode, "<<": IntBinopNode, @@ -14880,7 +14939,7 @@ def analyse_type_annotation(self, env, assigned_value=None): arg_type.create_declaration_utility_code(env) # Check for declaration modifiers, e.g. "typing.Optional[...]" or "dataclasses.InitVar[...]" - modifiers = annotation.analyse_pytyping_modifiers(env) if annotation.is_subscript else [] + modifiers = annotation.analyse_pytyping_modifiers(env) if annotation.is_subscript or isinstance(annotation, BitwiseOrNode) else [] return modifiers, arg_type diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7b052d9e1eb..7c329146fc7 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -786,9 +786,8 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co code.putln("#ifndef Py_PYTHON_H") code.putln(" #error Python headers needed to compile C extensions, " "please install development version of Python.") - code.putln("#elif PY_VERSION_HEX < 0x02070000 || " - "(0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)") - code.putln(" #error Cython requires Python 2.7+ or Python 3.3+.") + code.putln("#elif PY_VERSION_HEX < 0x03070000") + code.putln(" #error Cython requires Python 3.7+.") code.putln("#else") code.globalstate["end"].putln("#endif /* Py_PYTHON_H */") @@ -2682,10 +2681,10 @@ def generate_typeobj_spec(self, entry, code): # used in the non-limited API case, this doesn't preserve the weaklistoffset # from base classes. # Practically that doesn't matter, but it isn't exactly the identical. - code.putln('{"__weaklistoffset__", T_PYSSIZET, offsetof(%s, %s), READONLY},' + code.putln('{"__weaklistoffset__", T_PYSSIZET, offsetof(%s, %s), READONLY, 0},' % (objstruct, weakref_entry.cname)) code.putln("#endif") - code.putln("{0, 0, 0, 0}") + code.putln("{0, 0, 0, 0, 0}") code.putln("};") if weakref_entry: diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index b8b73c94da7..2231a06091a 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -201,3 +201,169 @@ def py_version_hex(major, minor=0, micro=0, release_level=0, release_serial=0): ('__pyx_CoroutineAwaitType', '__Pyx_Coroutine_USED'), ('__pyx_CoroutineType', '__Pyx_Coroutine_USED'), ] + + +iso_c23_keywords = frozenset(( + 'alignas', # (C23) + 'alignof', # (C23) + 'auto', + 'bool', # (C23) + 'break', + 'case', + 'char', + 'const', + 'constexpr', # (C23) + 'continue', + 'default', + 'do', + 'double', + 'else', + 'enum', + 'extern', + 'false', # (C23) + 'float', + 'for', + 'goto', + 'if', + 'inline', # (C99) + 'int', + 'long', + 'nullptr', # (C23) + 'register', + 'restrict', # (C99) + 'return', + 'short', + 'signed', + 'sizeof', + 'static', + 'static_assert', # (C23) + 'struct', + 'switch', + 'thread_local', # (C23) + 'true', # (C23) + 'typedef', + 'typeof', # (C23) + 'typeof_unqual', # (C23) + 'union', + 'unsigned', + 'void', + 'volatile', + 'while', + '_Alignas', # (C11) + '_Alignof', # (C11) + '_Atomic', # (C11) + '_BitInt', # (C23) + '_Bool', # (C99) + '_Complex', # (C99) + '_Decimal128', # (C23) + '_Decimal32', # (C23) + '_Decimal64', # (C23) + '_Generic', # (C11) + '_Imaginary', # (C99) + '_Noreturn', # (C11) + '_Static_assert', # (C11) + '_Thread_local', # (C11) +)) + + +iso_cpp23_keywords = frozenset(( + 'alignas', # (C++11) + 'alignof', # (C++11) + 'and', + 'and_eq', + 'asm', + 'atomic_cancel', # (TM TS) + 'atomic_commit', # (TM TS) + 'atomic_noexcept', # (TM TS) + 'auto', + 'bitand', + 'bitor', + 'bool', + 'break', + 'case', + 'catch', + 'char', + 'char8_t', # (C++20) + 'char16_t', # (C++11) + 'char32_t', # (C++11) + 'class', + 'compl', + 'concept', # (C++20) + 'const', + 'consteval', # (C++20) + 'constexpr', # (C++11) + 'constinit', # (C++20) + 'const_cast', + 'continue', + 'co_await', # (C++20) + 'co_return', # (C++20) + 'co_yield', # (C++20) + 'decltype', # (C++11) + 'default', + 'delete', + 'do', + 'double', + 'dynamic_cast', + 'else', + 'enum', + 'explicit', + 'export', + 'extern', + 'false', + 'float', + 'for', + 'friend', + 'goto', + 'if', + 'inline', + 'int', + 'long', + 'mutable', + 'namespace', + 'new', + 'noexcept', # (C++11) + 'not', + 'not_eq', + 'nullptr', # (C++11) + 'operator', + 'or', + 'or_eq', + 'private', + 'protected', + 'public', + 'reflexpr', # (reflection TS) + 'register', + 'reinterpret_cast', + 'requires', # (C++20) + 'return', + 'short', + 'signed', + 'sizeof', + 'static', + 'static_assert', # (C++11) + 'static_cast', + 'struct', + 'switch', + 'synchronized', # (TM TS) + 'template', + 'this', + 'thread_local', # (C++11) + 'throw', + 'true', + 'try', + 'typedef', + 'typeid', + 'typename', + 'union', + 'unsigned', + 'using', + 'virtual', + 'void', + 'volatile', + 'wchar_t', + 'while', + 'xor', + 'xor_eq', +)) + +reserved_cnames = iso_c23_keywords | iso_cpp23_keywords diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 28bb914e323..763a0498561 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -720,8 +720,10 @@ def analyse(self, return_type, env, nonempty=0, directive_locals=None, visibilit and not self.has_explicit_exc_clause and self.exception_check and visibility != 'extern'): + # If function is already declared from pxd, the exception_check has already correct value. + if not (self.declared_name() in env.entries and not in_pxd): + self.exception_check = False # implicit noexcept, with a warning - self.exception_check = False warning(self.pos, "Implicit noexcept declaration is deprecated." " Function declaration should contain 'noexcept' keyword.", @@ -3160,7 +3162,6 @@ def as_cfunction(self, cfunc=None, scope=None, overridable=True, returns=None, e if scope is None: scope = cfunc.scope cfunc_type = cfunc.type - has_explicit_exc_clause=True if len(self.args) != len(cfunc_type.args) or cfunc_type.has_varargs: error(self.pos, "wrong number of arguments") error(cfunc.pos, "previous declaration here") diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index bd6af1075f4..68f5ecf8d9f 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -2982,7 +2982,7 @@ def side_effect_free_reference(node, setting=False): index = LetRefNode(node.index) return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index] elif node.is_attribute: - obj, temps = side_effect_free_reference(node.obj) + obj, temps = side_effect_free_reference(node.obj, setting=setting) return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps elif isinstance(node, ExprNodes.BufferIndexNode): raise ValueError("Don't allow things like attributes of buffer indexing operations") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 7b01b782bd6..5a00c86122b 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1861,8 +1861,10 @@ def __getattr__(self, name): return getattr(self.cv_base_type, name) -def CConstType(base_type): - return CConstOrVolatileType(base_type, is_const=1) +def c_const_type(base_type): + """Creates a C 'const ...' type but does not test for 'error_type'. + """ + return CConstOrVolatileType(base_type, is_const=True) class FusedType(CType): @@ -4774,6 +4776,8 @@ def resolve(self): def specialize_here(self, pos, env, template_values=None): if len(template_values) != 1: + if self.modifier_name == "typing.Union": + return None error(pos, "'%s' takes exactly one template argument." % self.name) return error_type if template_values[0] is None: @@ -4850,13 +4854,13 @@ def specialize_here(self, pos, env, template_values=None): c_void_ptr_type = CPtrType(c_void_type) c_void_ptr_ptr_type = CPtrType(c_void_ptr_type) c_char_ptr_type = CPtrType(c_char_type) -c_const_char_ptr_type = CPtrType(CConstType(c_char_type)) +c_const_char_ptr_type = CPtrType(c_const_type(c_char_type)) c_uchar_ptr_type = CPtrType(c_uchar_type) -c_const_uchar_ptr_type = CPtrType(CConstType(c_uchar_type)) +c_const_uchar_ptr_type = CPtrType(c_const_type(c_uchar_type)) c_char_ptr_ptr_type = CPtrType(c_char_ptr_type) c_int_ptr_type = CPtrType(c_int_type) c_py_unicode_ptr_type = CPtrType(c_py_unicode_type) -c_const_py_unicode_ptr_type = CPtrType(CConstType(c_py_unicode_type)) +c_const_py_unicode_ptr_type = CPtrType(c_const_type(c_py_unicode_type)) c_py_ssize_t_ptr_type = CPtrType(c_py_ssize_t_type) c_ssize_t_ptr_type = CPtrType(c_ssize_t_type) c_size_t_ptr_type = CPtrType(c_size_t_type) @@ -5366,7 +5370,7 @@ def simple_c_type(signed, longness, name): # Returns None if arguments don't make sense. return modifiers_and_name_to_type.get((signed, longness, name)) -def parse_basic_type(name): +def parse_basic_type(name: str): base = None if name.startswith('p_'): base = parse_basic_type(name[2:]) @@ -5376,6 +5380,12 @@ def parse_basic_type(name): base = parse_basic_type(name[:-1]) if base: return CPtrType(base) + if name.startswith(('const_', 'volatile_')): + modifier, _, base_name = name.partition('_') + base = parse_basic_type(base_name) + if base: + return CConstOrVolatileType( + base, is_const=modifier == 'const', is_volatile=modifier == 'volatile') # if name in fixed_sign_int_types: return fixed_sign_int_types[name][1] @@ -5431,11 +5441,7 @@ def cpp_rvalue_ref_type(base_type): # Construct a C++ rvalue reference type return _construct_type_from_base(CppRvalueReferenceType, base_type) -def c_const_type(base_type): - # Construct a C const type. - return _construct_type_from_base(CConstType, base_type) - -def c_const_or_volatile_type(base_type, is_const, is_volatile): +def c_const_or_volatile_type(base_type, is_const=False, is_volatile=False): # Construct a C const/volatile type. return _construct_type_from_base(CConstOrVolatileType, base_type, is_const, is_volatile) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 991977bdc0b..d0c069bfa0d 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -21,24 +21,16 @@ from . import Code -iso_c99_keywords = { - 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', - 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', - 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', - 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', - 'volatile', 'while', - '_Bool', '_Complex'', _Imaginary', 'inline', 'restrict', -} - def c_safe_identifier(cname): # There are some C limitations on struct entry names. if ((cname[:2] == '__' and not (cname.startswith(Naming.pyrex_prefix) or cname in ('__weakref__', '__dict__'))) - or cname in iso_c99_keywords): + or cname in Naming.reserved_cnames): cname = Naming.pyrex_prefix + cname return cname + def punycodify_name(cname, mangle_with=None): # if passed the mangle_with should be a byte string # modified from PEP489 diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 4a3e441ebf8..cc1abcdfc19 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -987,10 +987,8 @@ def __init__(self, old_binops): MethodSlot(unaryfunc, "nb_index", "__index__", method_name_to_slot), # Added in release 3.5 - BinopSlot(bf, "nb_matrix_multiply", "__matmul__", method_name_to_slot, - ifdef="PY_VERSION_HEX >= 0x03050000"), - MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", method_name_to_slot, - ifdef="PY_VERSION_HEX >= 0x03050000"), + BinopSlot(bf, "nb_matrix_multiply", "__matmul__", method_name_to_slot), + MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", method_name_to_slot), ) self.PySequenceMethods = ( diff --git a/Cython/Includes/cpython/contextvars.pxd b/Cython/Includes/cpython/contextvars.pxd index 1ef9ac597b6..48b0e91c736 100644 --- a/Cython/Includes/cpython/contextvars.pxd +++ b/Cython/Includes/cpython/contextvars.pxd @@ -5,7 +5,7 @@ cdef extern from *: # Defining PyContextVar_Get() below to always return the default value for Py<3.7 and PyPy<7.3.6 # to make the inline functions sort-of work. """ - #if (PY_VERSION_HEX < 0x030700b1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030600)) && !defined(PyContextVar_Get) + #if (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030600) && !defined(PyContextVar_Get) #define PyContextVar_Get(var, d, v) \ ((d) ? \ ((void)(var), Py_INCREF(d), (v)[0] = (d), 0) : \ diff --git a/Cython/Includes/cpython/datetime.pxd b/Cython/Includes/cpython/datetime.pxd index 3dce3958882..21329d564a3 100644 --- a/Cython/Includes/cpython/datetime.pxd +++ b/Cython/Includes/cpython/datetime.pxd @@ -7,52 +7,14 @@ cdef extern from "Python.h": cdef extern from "datetime.h": """ - /* Backport for Python 2.x */ - #if PY_MAJOR_VERSION < 3 - #ifndef PyDateTime_DELTA_GET_DAYS - #define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) - #endif - #ifndef PyDateTime_DELTA_GET_SECONDS - #define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) - #endif - #ifndef PyDateTime_DELTA_GET_MICROSECONDS - #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) - #endif - #endif + #define __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold) \ + PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(year, month, day, hour, minute, second, \ + microsecond, tz, fold, PyDateTimeAPI->DateTimeType) + #define __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold) \ + PyDateTimeAPI->Time_FromTimeAndFold(hour, minute, second, microsecond, tz, fold, PyDateTimeAPI->TimeType) - /* Backport for Python < 3.6 */ - #if PY_VERSION_HEX < 0x030600a4 - #ifndef PyDateTime_TIME_GET_FOLD - #define PyDateTime_TIME_GET_FOLD(o) ((void)(o), 0) - #endif - #ifndef PyDateTime_DATE_GET_FOLD - #define PyDateTime_DATE_GET_FOLD(o) ((void)(o), 0) - #endif - #endif - - /* Backport for Python < 3.6 */ - #if PY_VERSION_HEX < 0x030600a4 - #define __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold) \ - ((void)(fold), PyDateTimeAPI->DateTime_FromDateAndTime(year, month, day, hour, minute, second, \ - microsecond, tz, PyDateTimeAPI->DateTimeType)) - #define __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold) \ - ((void)(fold), PyDateTimeAPI->Time_FromTime(hour, minute, second, microsecond, tz, PyDateTimeAPI->TimeType)) - #else /* For Python 3.6+ so that we can pass tz */ - #define __Pyx_DateTime_DateTimeWithFold(year, month, day, hour, minute, second, microsecond, tz, fold) \ - PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(year, month, day, hour, minute, second, \ - microsecond, tz, fold, PyDateTimeAPI->DateTimeType) - #define __Pyx_DateTime_TimeWithFold(hour, minute, second, microsecond, tz, fold) \ - PyDateTimeAPI->Time_FromTimeAndFold(hour, minute, second, microsecond, tz, fold, PyDateTimeAPI->TimeType) - #endif - - /* Backport for Python < 3.7 */ - #if PY_VERSION_HEX < 0x030700b1 - #define __Pyx_TimeZone_UTC NULL - #define __Pyx_TimeZone_FromOffsetAndName(offset, name) ((void)(offset), (void)(name), (PyObject*)NULL) - #else - #define __Pyx_TimeZone_UTC PyDateTime_TimeZone_UTC - #define __Pyx_TimeZone_FromOffsetAndName(offset, name) PyTimeZone_FromOffsetAndName(offset, name) - #endif + #define __Pyx_TimeZone_UTC PyDateTime_TimeZone_UTC + #define __Pyx_TimeZone_FromOffsetAndName(offset, name) PyTimeZone_FromOffsetAndName(offset, name) /* Backport for Python < 3.10 */ #if PY_VERSION_HEX < 0x030a00a1 @@ -304,8 +266,6 @@ cdef inline timedelta timedelta_new(int days, int seconds, int useconds): # Create timedelta object using DateTime CAPI factory function. cdef inline object timezone_new(object offset, object name=None): - if PY_VERSION_HEX < 0x030700b1: - raise RuntimeError('Time zones are not available from the C-API.') return __Pyx_TimeZone_FromOffsetAndName(offset, name if name is not None else NULL) # Create datetime object using DB API constructor. @@ -324,8 +284,6 @@ cdef inline date date_from_timestamp(timestamp): # Get UTC singleton cdef inline object get_utc(): - if PY_VERSION_HEX < 0x030700b1: - raise RuntimeError('Time zones are not available from the C-API.') return __Pyx_TimeZone_UTC # Get tzinfo of time diff --git a/Cython/Includes/cpython/memoryview.pxd b/Cython/Includes/cpython/memoryview.pxd index 83a84e6f911..8fbe547d294 100644 --- a/Cython/Includes/cpython/memoryview.pxd +++ b/Cython/Includes/cpython/memoryview.pxd @@ -6,27 +6,27 @@ cdef extern from "Python.h": # A memoryview object exposes the C level buffer interface as a Python # object which can then be passed around like any other object - object PyMemoryView_FromObject(object obj) + memoryview PyMemoryView_FromObject(object obj) # Return value: New reference. # Create a memoryview object from an object that provides the buffer # interface. If obj supports writable buffer exports, the memoryview object # will be read/write, otherwise it may be either read-only or read/write at # the discretion of the exporter. - object PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags) + memoryview PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags) # Return value: New reference. # Create a memoryview object using mem as the underlying buffer. flags can # be one of PyBUF_READ or PyBUF_WRITE. # New in version 3.3. - object PyMemoryView_FromBuffer(Py_buffer *view) + memoryview PyMemoryView_FromBuffer(Py_buffer *view) # Return value: New reference. # Create a memoryview object wrapping the given buffer structure view. For # simple byte buffers, PyMemoryView_FromMemory() is the preferred function. - object PyMemoryView_GetContiguous(object obj, - int buffertype, - char order) + memoryview PyMemoryView_GetContiguous(object obj, + int buffertype, + char order) # Return value: New reference. # Create a memoryview object to a contiguous chunk of memory (in either ‘C’ # or ‘F’ortran order) from an object that defines the buffer interface. If diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 6a6c8fd3ab1..01ef8408211 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -274,7 +274,8 @@ def __eq__(self, value): return not self._items and not value._items def __repr__(self): - return "%s *" % (self._basetype,) + return f"{self._basetype} *" + class ArrayType(PointerType): @@ -353,16 +354,30 @@ def __setattr__(self, key, value): else: raise AttributeError("Union has no member '%s'" % key) -def pointer(basetype): - class PointerInstance(PointerType): - _basetype = basetype - return PointerInstance -def array(basetype, n): - class ArrayInstance(ArrayType): - _basetype = basetype - _n = n - return ArrayInstance +class pointer(PointerType): + # Implemented as class to support both 'pointer(int)' and 'pointer[int]'. + def __new__(cls, basetype): + class PointerInstance(PointerType): + _basetype = basetype + return PointerInstance + + def __class_getitem__(cls, basetype): + return cls(basetype) + + +class array(ArrayType): + # Implemented as class to support both 'array(int, 5)' and 'array[int, 5]'. + def __new__(cls, basetype, n): + class ArrayInstance(ArrayType): + _basetype = basetype + _n = n + return ArrayInstance + + def __class_getitem__(cls, item): + basetype, n = item + return cls(basetype, item) + def struct(**members): class StructInstance(StructType): @@ -378,6 +393,7 @@ class UnionInstance(UnionType): setattr(UnionInstance, key, None) return UnionInstance + class typedef(CythonType): def __init__(self, type, name=None): @@ -393,6 +409,25 @@ def __repr__(self): __getitem__ = index_type + +class const(typedef): + def __init__(self, type, name=None): + name = f"const {name or repr(type)}" + super().__init__(type, name) + + def __class_getitem__(cls, base_type): + return const(base_type) + + +class volatile(typedef): + def __init__(self, type, name=None): + name = f"volatile {name or repr(type)}" + super().__init__(type, name) + + def __class_getitem__(cls, base_type): + return volatile(base_type) + + class _FusedType(CythonType): __getitem__ = index_type @@ -493,18 +528,28 @@ def _specialized_from_args(signatures, args, kwargs): void = typedef(None, "void") Py_tss_t = typedef(None, "Py_tss_t") -for t in int_types: - for i in range(1, 4): - gs["%s_%s" % ('p'*i, t)] = gs[t]._pointer(i) - if 'u'+t in gs: - gs["%s_u%s" % ('p'*i, t)] = gs['u'+t]._pointer(i) - gs["%s_s%s" % ('p'*i, t)] = gs['s'+t]._pointer(i) +# Generate const types. +for t in int_types + float_types + complex_types + other_types: + for t in (t, f'u{t}', f's{t}'): + if t in gs: + gs[f"const_{t}"] = const(gs[t], t) + +# Generate pointer types: p_int, p_const_char, etc. +for i in range(1, 4): + for const_ in ('', 'const_'): + for t in int_types: + for t in (t, f'u{t}', f's{t}'): + if t in gs: + gs[f"{'p'*i}_{const_}{t}"] = pointer(gs[f"{'p'*(i-1)}{'_' if i > 1 else ''}{const_}{t}"]) + + for t in float_types + complex_types: + gs[f"{'p'*i}_{const_}{t}"] = pointer(gs[f"{'p'*(i-1)}{'_' if i > 1 else ''}{const_}{t}"]) -for t in float_types + complex_types + other_types: - for i in range(1, 4): - gs["%s_%s" % ('p'*i, t)] = gs[t]._pointer(i) + gs[f"{'p'*i}_const_bint"] = pointer(gs[f"{'p'*(i-1)}{'_' if i > 1 else ''}const_bint"]) + for t in other_types: + gs[f"{'p'*i}_{t}"] = pointer(gs[f"{'p'*(i-1)}{'_' if i > 1 else ''}{t}"]) -del t, i +del t, const_, i NULL = gs['p_void'](0) diff --git a/Cython/Shadow.pyi b/Cython/Shadow.pyi index 8bde351c74d..141c9224279 100644 --- a/Cython/Shadow.pyi +++ b/Cython/Shadow.pyi @@ -2,7 +2,10 @@ import dataclasses as dataclasses from builtins import (int as py_int, float as py_float, bool as py_bool, str as py_str, complex as py_complex) from types import TracebackType -from typing import Any, Iterable, Sequence, Optional, Type, TypeVar, Generic, Callable, overload +from typing import ( + Any, Iterable, Sequence, Optional, Type, TypeVar, Generic, Callable, overload, + TypeAlias, Annotated, +) # Type checkers assume typing_extensions is always present from typing_extensions import Literal, ParamSpec, overload, final as final @@ -15,45 +18,14 @@ __version__: str # Predefined types -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 = py_int | str Py_UNICODE = 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 = Type[None] basestring = py_str unicode = py_str -gs: dict[str, Any] # Should match the return type of globals() compiled: bool @@ -108,17 +80,22 @@ def _compiler_directive(__val: bool = ...) -> _Decorator: ... # These all come from 'Compiler directives' on Source Files and Compilation. # The following directives are missing as they need to be global: -# - annotation_typing # - c_string_type # - c_string_encoding # Note that c_api_binop_methods and type_version_tag is defined above. -boundscheck = wraparound = initializedcheck = nonecheck = cdivision = \ - cdivision_warnings = profile = linetrace = infer_types = \ - emit_code_comments = _empty_decorator_and_manager +annotation_typing = returns = wraparound = boundscheck = initializedcheck = \ + nonecheck = cdivision = cdivision_warnings = \ + profile = linetrace = infer_types = \ + freelist = auto_pickle = cpow = trashcan = \ + auto_cpdef = c_api_binop_methods = \ + allow_none_for_extension_args = callspec = show_performance_hints = \ + cpp_locals = py2_import = iterable_coroutine = remove_unreachable = \ + _empty_decorator_and_manager binding = embedsignature = always_allow_keywords = unraisable_tracebacks = \ - iterable_coroutine = cpp_locals = _compiler_directive + cpp_locals = \ + _compiler_directive # overflowcheck() has to be specialized because there is also overflowcheck.fold class _OverflowcheckClass: @@ -180,6 +157,9 @@ def typeof(__obj: object) -> str: ... def address(__obj: object) -> PointerType: ... +const: TypeAlias = Annotated[_T, "cython.const"] +volatile: TypeAlias = Annotated[_T, "cython.volatile"] + @overload def declare( @@ -262,9 +242,13 @@ class ArrayType(PointerType[_T]): def index_type( base_type: _T, item: tuple | slice | int) -> _ArrayType[_T]: ... -def pointer(basetype: _T) -> Type[PointerType[_T]]: ... +class pointer(PointerType[_T]): + def __new__(cls, basetype: _T) -> Type[PointerType[_T]]: ... + def __class_getitem__(cls, basetype: _T) -> Type[PointerType[_T]]: ... -def array(basetype: _T, n: int) -> Type[ArrayType[_T]]: ... +class array(ArrayType[_T]): + def __new__(basetype: _T, n: int) -> Type[ArrayType[_T, int]]: ... + def __class_getitem__(cls, item: tuple[_T, int]) -> Type[ArrayType[_T, int]]: ... def struct(**members: type) -> Type[Any]: ... @@ -280,3 +264,258 @@ class typedef(CythonType, Generic[_T]): def __call__(self, *arg: Any) -> _T: ... def __repr__(self) -> str: ... __getitem__ = index_type + +NULL: pointer[Any] + +##### START: GENERATED LIST OF GENERATED TYPES ##### +# Generated by "Tools/cython-generate-shadow-pyi.py" on 2024-09-24 11:45:22.474391 + +const_bint : TypeAlias = const[bint] +p_const_bint = pointer[const[bint]] +pp_const_bint = pointer[pointer[const[bint]]] +ppp_const_bint = pointer[pointer[pointer[const[bint]]]] +p_bint = pointer[bint] +pp_bint = pointer[pointer[bint]] +ppp_bint = pointer[pointer[pointer[bint]]] +char : TypeAlias = py_int +const_char : TypeAlias = const[py_int] +p_const_char : TypeAlias = pointer[const[py_int]] +pp_const_char = pointer[pointer[const[py_int]]] +ppp_const_char = pointer[pointer[pointer[const[py_int]]]] +p_char = pointer[py_int] +pp_char = pointer[pointer[py_int]] +ppp_char = pointer[pointer[pointer[py_int]]] +complex : TypeAlias = py_complex +const_complex : TypeAlias = const[py_complex] +p_const_complex = pointer[const[py_complex]] +pp_const_complex = pointer[pointer[const[py_complex]]] +ppp_const_complex = pointer[pointer[pointer[const[py_complex]]]] +p_complex = pointer[py_complex] +pp_complex = pointer[pointer[py_complex]] +ppp_complex = pointer[pointer[pointer[py_complex]]] +double : TypeAlias = py_float +const_double : TypeAlias = const[py_float] +p_const_double = pointer[const[py_float]] +pp_const_double = pointer[pointer[const[py_float]]] +ppp_const_double = pointer[pointer[pointer[const[py_float]]]] +p_double = pointer[py_float] +pp_double = pointer[pointer[py_float]] +ppp_double = pointer[pointer[pointer[py_float]]] +doublecomplex : TypeAlias = py_complex +const_doublecomplex : TypeAlias = const[py_complex] +p_const_doublecomplex = pointer[const[py_complex]] +pp_const_doublecomplex = pointer[pointer[const[py_complex]]] +ppp_const_doublecomplex = pointer[pointer[pointer[const[py_complex]]]] +p_doublecomplex = pointer[py_complex] +pp_doublecomplex = pointer[pointer[py_complex]] +ppp_doublecomplex = pointer[pointer[pointer[py_complex]]] +float : TypeAlias = py_float +const_float : TypeAlias = const[py_float] +p_const_float = pointer[const[py_float]] +pp_const_float = pointer[pointer[const[py_float]]] +ppp_const_float = pointer[pointer[pointer[const[py_float]]]] +p_float = pointer[py_float] +pp_float = pointer[pointer[py_float]] +ppp_float = pointer[pointer[pointer[py_float]]] +floatcomplex : TypeAlias = py_complex +const_floatcomplex : TypeAlias = const[py_complex] +p_const_floatcomplex = pointer[const[py_complex]] +pp_const_floatcomplex = pointer[pointer[const[py_complex]]] +ppp_const_floatcomplex = pointer[pointer[pointer[const[py_complex]]]] +p_floatcomplex = pointer[py_complex] +pp_floatcomplex = pointer[pointer[py_complex]] +ppp_floatcomplex = pointer[pointer[pointer[py_complex]]] +int : TypeAlias = py_int +const_int : TypeAlias = const[py_int] +p_const_int = pointer[const[py_int]] +pp_const_int = pointer[pointer[const[py_int]]] +ppp_const_int = pointer[pointer[pointer[const[py_int]]]] +p_int = pointer[py_int] +pp_int = pointer[pointer[py_int]] +ppp_int = pointer[pointer[pointer[py_int]]] +long : TypeAlias = py_int +const_long : TypeAlias = const[py_int] +p_const_long = pointer[const[py_int]] +pp_const_long = pointer[pointer[const[py_int]]] +ppp_const_long = pointer[pointer[pointer[const[py_int]]]] +p_long = pointer[py_int] +pp_long = pointer[pointer[py_int]] +ppp_long = pointer[pointer[pointer[py_int]]] +py_long : TypeAlias = py_int +longdouble : TypeAlias = py_float +const_longdouble : TypeAlias = const[py_float] +p_const_longdouble = pointer[const[py_float]] +pp_const_longdouble = pointer[pointer[const[py_float]]] +ppp_const_longdouble = pointer[pointer[pointer[const[py_float]]]] +p_longdouble = pointer[py_float] +pp_longdouble = pointer[pointer[py_float]] +ppp_longdouble = pointer[pointer[pointer[py_float]]] +longdoublecomplex : TypeAlias = py_complex +const_longdoublecomplex : TypeAlias = const[py_complex] +p_const_longdoublecomplex = pointer[const[py_complex]] +pp_const_longdoublecomplex = pointer[pointer[const[py_complex]]] +ppp_const_longdoublecomplex = pointer[pointer[pointer[const[py_complex]]]] +p_longdoublecomplex = pointer[py_complex] +pp_longdoublecomplex = pointer[pointer[py_complex]] +ppp_longdoublecomplex = pointer[pointer[pointer[py_complex]]] +longlong : TypeAlias = py_int +const_longlong : TypeAlias = const[py_int] +p_const_longlong = pointer[const[py_int]] +pp_const_longlong = pointer[pointer[const[py_int]]] +ppp_const_longlong = pointer[pointer[pointer[const[py_int]]]] +p_longlong = pointer[py_int] +pp_longlong = pointer[pointer[py_int]] +ppp_longlong = pointer[pointer[pointer[py_int]]] +schar : TypeAlias = py_int +const_schar : TypeAlias = const[py_int] +p_const_schar = pointer[const[py_int]] +pp_const_schar = pointer[pointer[const[py_int]]] +ppp_const_schar = pointer[pointer[pointer[const[py_int]]]] +p_schar = pointer[py_int] +pp_schar = pointer[pointer[py_int]] +ppp_schar = pointer[pointer[pointer[py_int]]] +short : TypeAlias = py_int +const_short : TypeAlias = const[py_int] +p_const_short = pointer[const[py_int]] +pp_const_short = pointer[pointer[const[py_int]]] +ppp_const_short = pointer[pointer[pointer[const[py_int]]]] +p_short = pointer[py_int] +pp_short = pointer[pointer[py_int]] +ppp_short = pointer[pointer[pointer[py_int]]] +sint : TypeAlias = py_int +const_sint : TypeAlias = const[py_int] +p_const_sint = pointer[const[py_int]] +pp_const_sint = pointer[pointer[const[py_int]]] +ppp_const_sint = pointer[pointer[pointer[const[py_int]]]] +p_sint = pointer[py_int] +pp_sint = pointer[pointer[py_int]] +ppp_sint = pointer[pointer[pointer[py_int]]] +slong : TypeAlias = py_int +const_slong : TypeAlias = const[py_int] +p_const_slong = pointer[const[py_int]] +pp_const_slong = pointer[pointer[const[py_int]]] +ppp_const_slong = pointer[pointer[pointer[const[py_int]]]] +p_slong = pointer[py_int] +pp_slong = pointer[pointer[py_int]] +ppp_slong = pointer[pointer[pointer[py_int]]] +slonglong : TypeAlias = py_int +const_slonglong : TypeAlias = const[py_int] +p_const_slonglong = pointer[const[py_int]] +pp_const_slonglong = pointer[pointer[const[py_int]]] +ppp_const_slonglong = pointer[pointer[pointer[const[py_int]]]] +p_slonglong = pointer[py_int] +pp_slonglong = pointer[pointer[py_int]] +ppp_slonglong = pointer[pointer[pointer[py_int]]] +sshort : TypeAlias = py_int +const_sshort : TypeAlias = const[py_int] +p_const_sshort = pointer[const[py_int]] +pp_const_sshort = pointer[pointer[const[py_int]]] +ppp_const_sshort = pointer[pointer[pointer[const[py_int]]]] +p_sshort = pointer[py_int] +pp_sshort = pointer[pointer[py_int]] +ppp_sshort = pointer[pointer[pointer[py_int]]] +Py_hash_t : TypeAlias = py_int +const_Py_hash_t : TypeAlias = const[py_int] +p_const_Py_hash_t = pointer[const[py_int]] +pp_const_Py_hash_t = pointer[pointer[const[py_int]]] +ppp_const_Py_hash_t = pointer[pointer[pointer[const[py_int]]]] +p_Py_hash_t = pointer[py_int] +pp_Py_hash_t = pointer[pointer[py_int]] +ppp_Py_hash_t = pointer[pointer[pointer[py_int]]] +ptrdiff_t : TypeAlias = py_int +const_ptrdiff_t : TypeAlias = const[py_int] +p_const_ptrdiff_t = pointer[const[py_int]] +pp_const_ptrdiff_t = pointer[pointer[const[py_int]]] +ppp_const_ptrdiff_t = pointer[pointer[pointer[const[py_int]]]] +p_ptrdiff_t = pointer[py_int] +pp_ptrdiff_t = pointer[pointer[py_int]] +ppp_ptrdiff_t = pointer[pointer[pointer[py_int]]] +size_t : TypeAlias = py_int +const_size_t : TypeAlias = const[py_int] +p_const_size_t = pointer[const[py_int]] +pp_const_size_t = pointer[pointer[const[py_int]]] +ppp_const_size_t = pointer[pointer[pointer[const[py_int]]]] +p_size_t = pointer[py_int] +pp_size_t = pointer[pointer[py_int]] +ppp_size_t = pointer[pointer[pointer[py_int]]] +ssize_t : TypeAlias = py_int +const_ssize_t : TypeAlias = const[py_int] +p_const_ssize_t = pointer[const[py_int]] +pp_const_ssize_t = pointer[pointer[const[py_int]]] +ppp_const_ssize_t = pointer[pointer[pointer[const[py_int]]]] +p_ssize_t = pointer[py_int] +pp_ssize_t = pointer[pointer[py_int]] +ppp_ssize_t = pointer[pointer[pointer[py_int]]] +Py_ssize_t : TypeAlias = py_int +const_Py_ssize_t : TypeAlias = const[py_int] +p_const_Py_ssize_t = pointer[const[py_int]] +pp_const_Py_ssize_t = pointer[pointer[const[py_int]]] +ppp_const_Py_ssize_t = pointer[pointer[pointer[const[py_int]]]] +p_Py_ssize_t = pointer[py_int] +pp_Py_ssize_t = pointer[pointer[py_int]] +ppp_Py_ssize_t = pointer[pointer[pointer[py_int]]] +Py_tss_t : TypeAlias = Any +const_Py_tss_t : TypeAlias = const[Any] +p_Py_tss_t = pointer[Any] +pp_Py_tss_t = pointer[pointer[Any]] +ppp_Py_tss_t = pointer[pointer[pointer[Any]]] +uchar : TypeAlias = py_int +const_uchar : TypeAlias = const[py_int] +p_const_uchar = pointer[const[py_int]] +pp_const_uchar = pointer[pointer[const[py_int]]] +ppp_const_uchar = pointer[pointer[pointer[const[py_int]]]] +p_uchar = pointer[py_int] +pp_uchar = pointer[pointer[py_int]] +ppp_uchar = pointer[pointer[pointer[py_int]]] +const_Py_UCS4 : TypeAlias = const[py_int] +p_const_Py_UCS4 = pointer[const[py_int]] +pp_const_Py_UCS4 = pointer[pointer[const[py_int]]] +ppp_const_Py_UCS4 = pointer[pointer[pointer[const[py_int]]]] +p_Py_UCS4 = pointer[py_int] +pp_Py_UCS4 = pointer[pointer[py_int]] +ppp_Py_UCS4 = pointer[pointer[pointer[py_int]]] +uint : TypeAlias = py_int +const_uint : TypeAlias = const[py_int] +p_const_uint = pointer[const[py_int]] +pp_const_uint = pointer[pointer[const[py_int]]] +ppp_const_uint = pointer[pointer[pointer[const[py_int]]]] +p_uint = pointer[py_int] +pp_uint = pointer[pointer[py_int]] +ppp_uint = pointer[pointer[pointer[py_int]]] +ulong : TypeAlias = py_int +const_ulong : TypeAlias = const[py_int] +p_const_ulong = pointer[const[py_int]] +pp_const_ulong = pointer[pointer[const[py_int]]] +ppp_const_ulong = pointer[pointer[pointer[const[py_int]]]] +p_ulong = pointer[py_int] +pp_ulong = pointer[pointer[py_int]] +ppp_ulong = pointer[pointer[pointer[py_int]]] +ulonglong : TypeAlias = py_int +const_ulonglong : TypeAlias = const[py_int] +p_const_ulonglong = pointer[const[py_int]] +pp_const_ulonglong = pointer[pointer[const[py_int]]] +ppp_const_ulonglong = pointer[pointer[pointer[const[py_int]]]] +p_ulonglong = pointer[py_int] +pp_ulonglong = pointer[pointer[py_int]] +ppp_ulonglong = pointer[pointer[pointer[py_int]]] +const_Py_UNICODE : TypeAlias = const[py_int] +p_const_Py_UNICODE = pointer[const[py_int]] +pp_const_Py_UNICODE = pointer[pointer[const[py_int]]] +ppp_const_Py_UNICODE = pointer[pointer[pointer[const[py_int]]]] +p_Py_UNICODE = pointer[py_int] +pp_Py_UNICODE = pointer[pointer[py_int]] +ppp_Py_UNICODE = pointer[pointer[pointer[py_int]]] +ushort : TypeAlias = py_int +const_ushort : TypeAlias = const[py_int] +p_const_ushort = pointer[const[py_int]] +pp_const_ushort = pointer[pointer[const[py_int]]] +ppp_const_ushort = pointer[pointer[pointer[const[py_int]]]] +p_ushort = pointer[py_int] +pp_ushort = pointer[pointer[py_int]] +ppp_ushort = pointer[pointer[pointer[py_int]]] +const_void : TypeAlias = const[Any] +p_void = pointer[Any] +pp_void = pointer[pointer[Any]] +ppp_void = pointer[pointer[pointer[Any]]] + +##### END: GENERATED LIST OF GENERATED TYPES ##### diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index fcbf7c432dd..3884c6d5a7e 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -1621,7 +1621,7 @@ static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit( gen->classobj = NULL; gen->yieldfrom = NULL; gen->yieldfrom_am_send = NULL; - #if PY_VERSION_HEX >= 0x030B00a4 && CYTHON_COMPILING_IN_CPYTHON + #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_LIMITED_API gen->gi_exc_state.exc_value = NULL; #else gen->gi_exc_state.exc_type = NULL; diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 73cb8300174..c8febad25de 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -13,10 +13,7 @@ if (likely(__Pyx_init_assertions_enabled() == 0)); else /////////////// AssertionsEnabled.proto /////////////// -#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag) - #define __Pyx_init_assertions_enabled() (0) - #define __pyx_assertions_enabled() (1) -#elif CYTHON_COMPILING_IN_LIMITED_API || (CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030C0000) +#if CYTHON_COMPILING_IN_LIMITED_API || (CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030C0000) // Py_OptimizeFlag is deprecated in Py3.12+ and not available in the Limited API. static int __pyx_assertions_enabled_flag; #define __pyx_assertions_enabled() (__pyx_assertions_enabled_flag) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 009a622d4f0..3f2a5b32f9e 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -2477,16 +2477,6 @@ static int __Pyx_VersionSanityCheck(void) { return 0; } -/////////////////////////// AccessPyMutexForFreeThreading.proto //////////// - -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING -// TODO - this is likely to get exposed properly at some point -#ifndef Py_BUILD_CORE -#define Py_BUILD_CORE 1 -#endif -#include "internal/pycore_lock.h" -#endif - ////////////////////////// SharedInFreeThreading.proto ////////////////// #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING diff --git a/Demos/callback/cheese.pyx b/Demos/callback/cheese.pyx index 67d556ab71a..24cb0a8bc64 100644 --- a/Demos/callback/cheese.pyx +++ b/Demos/callback/cheese.pyx @@ -9,5 +9,5 @@ cdef extern from "cheesefinder.h": def find(f): find_cheeses(callback, f) -cdef void callback(char *name, void *f): +cdef void callback(char *name, void *f) noexcept: (f)(name.decode('utf-8')) diff --git a/Tools/ci-run.sh b/Tools/ci-run.sh index 37d50c0589e..c50022e0319 100644 --- a/Tools/ci-run.sh +++ b/Tools/ci-run.sh @@ -2,7 +2,7 @@ set -x -GCC_VERSION=${GCC_VERSION:=8} +GCC_VERSION=${GCC_VERSION:=10} # Set up compilers if [[ $TEST_CODE_STYLE == "1" ]]; then diff --git a/bin/cython-generate-lexicon.py b/Tools/cython-generate-lexicon.py similarity index 100% rename from bin/cython-generate-lexicon.py rename to Tools/cython-generate-lexicon.py diff --git a/Tools/cython-generate-shadow-pyi.py b/Tools/cython-generate-shadow-pyi.py new file mode 100755 index 00000000000..9f9f46a2867 --- /dev/null +++ b/Tools/cython-generate-shadow-pyi.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +import re +from datetime import datetime + +import cython + +GEN_START = "##### START: GENERATED LIST OF GENERATED TYPES #####\n" +GEN_END = "##### END: GENERATED LIST OF GENERATED TYPES #####\n" + +map_py_type_to_name = { + bool: 'bint', + float: 'py_float', + int: 'py_int', + complex: 'py_complex', + None: 'Any', +}.get + + +def non_generated_lines(lines, start=GEN_START, end=GEN_END): + lines = iter(lines) + for line in lines: + if line == start: + for line in lines: + if line == end: + break + continue + yield line + + +def find_known_names(file_path): + match_name = re.compile(r"(?:(?:class|type)\s+)?(\w+)\s*[=:\[](?:\w|\s)").match + + with open(file_path) as f: + return { + match.group(1) + for match in map(match_name, non_generated_lines(f)) + if match is not None + } + + +def replace_type_list(file_path, type_lines): + with open(file_path) as f: + lines = f.readlines() + + try: + start_index = lines.index(GEN_START) + end_index = lines.index(GEN_END, start_index) + except ValueError: + raise RuntimeError(f"Failed to find generated section in {file_path}") + + new_lines = lines[:start_index+1] + new_lines.append(f'# Generated by "Tools/cython-generate-shadow-pyi.py" on {datetime.now()}\n') + new_lines.append("\n") + + new_lines.extend(type_lines) + + new_lines.append("\n") + new_lines.extend(lines[end_index:]) + + if lines[start_index+2:] == new_lines[start_index+2:]: + # No changes except for the generation date => don't change file. + return + + with open(file_path, 'w') as f: + f.writelines(new_lines) + + +def map_type(pytype): + try: + py_type_name = map_py_type_to_name(pytype) + if py_type_name is not None: + return py_type_name + except TypeError: + # Type is not hashable. + pass + + if isinstance(pytype, (cython.const, cython.volatile)): + return f"{type(pytype).__name__}[{map_type(pytype._basetype)}]" + + if isinstance(pytype, cython.typedef): + return map_type(pytype._basetype) + + if isinstance(pytype, type) and issubclass(pytype, cython.PointerType): + base_type = map_type(pytype._basetype) + if issubclass(pytype, cython.ArrayType): + return f"array[{base_type}, {pytype._n}]" + else: + return f"pointer[{base_type}]" + + raise ValueError(f"Unmappable type '{pytype}({type(pytype).__mro__})") + + +def map_types(namespace, ignore=()): + for type_name, pytype in namespace.items(): + if type_name in ignore: + continue + if type_name.startswith('_'): + continue + is_type_alias = isinstance(pytype, cython.typedef) + if not (is_type_alias or isinstance(pytype, type)): + continue + + try: + py_type_name = map_type(pytype) + except (ValueError, AttributeError, TypeError): + print(f"Not mapping type '{type_name}' from {pytype}") + continue + + if type_name == py_type_name: + continue + + yield type_name, py_type_name, is_type_alias + + +def generate_type_lines(types): + # Sort 'pointers_const_type_name' by (type name, const, pointers) + def sort_key(item): + return '_'.join(reversed(item[0].lower().split('_'))) + + for type_name, py_type_code, is_type_alias in sorted(types, key=sort_key): + yield f"{type_name}{' : TypeAlias' if is_type_alias else ''} = {py_type_code}\n" + + +def main(file_path): + namespace = vars(cython) + declared_names = find_known_names(file_path) + + types = map_types(namespace, ignore=declared_names) + + type_lines = generate_type_lines(types) + replace_type_list(file_path, type_lines) + + +if __name__ == "__main__": + import sys + shadow_py = sys.argv[1] if len(sys.argv) > 1 else "Cython/Shadow.pyi" + main(shadow_py) diff --git a/docs/examples/tutorial/clibraries/queue.py b/docs/examples/tutorial/clibraries/queue.py index e99b9b32c32..68853f49312 100644 --- a/docs/examples/tutorial/clibraries/queue.py +++ b/docs/examples/tutorial/clibraries/queue.py @@ -2,7 +2,7 @@ @cython.cclass class Queue: - _c_queue: cython.pointer(cqueue.Queue) + _c_queue: cython.pointer[cqueue.Queue] def __cinit__(self): self._c_queue = cqueue.queue_new() diff --git a/docs/examples/tutorial/clibraries/queue2.py b/docs/examples/tutorial/clibraries/queue2.py index de6d58a9931..ddea2dbbed3 100644 --- a/docs/examples/tutorial/clibraries/queue2.py +++ b/docs/examples/tutorial/clibraries/queue2.py @@ -2,7 +2,7 @@ @cython.cclass class Queue: - _c_queue = cython.declare(cython.pointer(cqueue.Queue)) + _c_queue = cython.declare(cython.pointer[cqueue.Queue]) def __cinit__(self): self._c_queue = cqueue.queue_new() diff --git a/docs/examples/tutorial/clibraries/queue3.py b/docs/examples/tutorial/clibraries/queue3.py index 79f34125498..a4351f03d72 100644 --- a/docs/examples/tutorial/clibraries/queue3.py +++ b/docs/examples/tutorial/clibraries/queue3.py @@ -12,7 +12,7 @@ class Queue: >>> q.pop() 5 """ - _c_queue = cython.declare(cython.pointer(cqueue.Queue)) + _c_queue = cython.declare(cython.pointer[cqueue.Queue]) def __cinit__(self): self._c_queue = cqueue.queue_new() if self._c_queue is cython.NULL: diff --git a/docs/examples/tutorial/parallelization/median.py b/docs/examples/tutorial/parallelization/median.py index 535a2b1366f..19469f0672a 100644 --- a/docs/examples/tutorial/parallelization/median.py +++ b/docs/examples/tutorial/parallelization/median.py @@ -14,12 +14,12 @@ def median_along_axis0(x: cython.double[:,:]): out: cython.double[::1] = np.empty(x.shape[1]) i: cython.Py_ssize_t j: cython.Py_ssize_t - scratch: cython.pointer(cython.double) - median_it: cython.pointer(cython.double) + scratch: cython.p_double + median_it: cython.p_double with cython.nogil, parallel(): # allocate scratch space per loop scratch = cython.cast( - cython.pointer(cython.double), + cython.p_double, malloc(cython.sizeof(cython.double)*x.shape[0])) try: for i in prange(x.shape[1]): diff --git a/docs/examples/tutorial/string/c_func.py b/docs/examples/tutorial/string/c_func.py index a6389b11f58..55944b863c3 100644 --- a/docs/examples/tutorial/string/c_func.py +++ b/docs/examples/tutorial/string/c_func.py @@ -17,7 +17,7 @@ def c_call_returning_a_c_string() -> cython.p_char: @cython.cfunc def get_a_c_string(c_string_ptr: cython.pp_char, - length: cython.pointer(cython.Py_ssize_t)) -> cython.int: + length: cython.p_Py_ssize_t) -> cython.int: c_string_ptr[0] = cython.cast(cython.p_char, malloc( (n + 1) * cython.sizeof(cython.char))) diff --git a/docs/examples/userguide/buffer/matrix_with_buffer.py b/docs/examples/userguide/buffer/matrix_with_buffer.py index 34ccc659147..d2f021235c2 100644 --- a/docs/examples/userguide/buffer/matrix_with_buffer.py +++ b/docs/examples/userguide/buffer/matrix_with_buffer.py @@ -16,7 +16,7 @@ def add_row(self): """Adds a row, initially zero-filled.""" self.v.resize(self.v.size() + self.ncols) - def __getbuffer__(self, buffer: cython.pointer(Py_buffer), flags: cython.int): + def __getbuffer__(self, buffer: cython.pointer[Py_buffer], flags: cython.int): itemsize: cython.Py_ssize_t = cython.sizeof(self.v[0]) self.shape[0] = self.v.size() // self.ncols @@ -44,5 +44,5 @@ def __getbuffer__(self, buffer: cython.pointer(Py_buffer), flags: cython.int): buffer.strides = self.strides buffer.suboffsets = cython.NULL # for pointer arrays only - def __releasebuffer__(self, buffer: cython.pointer(Py_buffer)): + def __releasebuffer__(self, buffer: cython.pointer[Py_buffer]): pass diff --git a/docs/examples/userguide/buffer/view_count.py b/docs/examples/userguide/buffer/view_count.py index 6a0554abc03..7caaf77c0ed 100644 --- a/docs/examples/userguide/buffer/view_count.py +++ b/docs/examples/userguide/buffer/view_count.py @@ -21,10 +21,10 @@ def add_row(self): raise ValueError("can't add row while being viewed") self.v.resize(self.v.size() + self.ncols) - def __getbuffer__(self, buffer: cython.pointer(Py_buffer), flags: cython.int): + def __getbuffer__(self, buffer: cython.pointer[Py_buffer], flags: cython.int): # ... as before self.view_count += 1 - def __releasebuffer__(self, buffer: cython.pointer(Py_buffer)): + def __releasebuffer__(self, buffer: cython.pointer[Py_buffer]): self.view_count -= 1 diff --git a/docs/examples/userguide/extension_types/owned_pointer.py b/docs/examples/userguide/extension_types/owned_pointer.py index 1c235a8837d..37c93b8d949 100644 --- a/docs/examples/userguide/extension_types/owned_pointer.py +++ b/docs/examples/userguide/extension_types/owned_pointer.py @@ -3,7 +3,7 @@ @cython.cclass class OwnedPointer: - ptr: cython.pointer(cython.void) + ptr: cython.p_void def __dealloc__(self): if self.ptr is not cython.NULL: @@ -11,7 +11,7 @@ def __dealloc__(self): @staticmethod @cython.cfunc - def create(ptr: cython.pointer(cython.void)): + def create(ptr: cython.p_void): p = OwnedPointer() p.ptr = ptr return p diff --git a/docs/examples/userguide/extension_types/wrapper_class.py b/docs/examples/userguide/extension_types/wrapper_class.py index b625ffebd7e..1de4ea165f7 100644 --- a/docs/examples/userguide/extension_types/wrapper_class.py +++ b/docs/examples/userguide/extension_types/wrapper_class.py @@ -10,7 +10,7 @@ @cython.cclass class WrapperClass: """A wrapper class for a C/C++ data structure""" - _ptr: cython.pointer(my_c_struct) + _ptr: cython.pointer[my_c_struct] ptr_owner: cython.bint def __cinit__(self): @@ -38,7 +38,7 @@ def b(self): @staticmethod @cython.cfunc - def from_ptr(_ptr: cython.pointer(my_c_struct), owner: cython.bint=False) -> WrapperClass: + def from_ptr(_ptr: cython.pointer[my_c_struct], owner: cython.bint=False) -> WrapperClass: """Factory function to create WrapperClass objects from given my_c_struct pointer. @@ -56,8 +56,8 @@ def from_ptr(_ptr: cython.pointer(my_c_struct), owner: cython.bint=False) -> Wra def new_struct() -> WrapperClass: """Factory function to create WrapperClass objects with newly allocated my_c_struct""" - _ptr: cython.pointer(my_c_struct) = cython.cast( - cython.pointer(my_c_struct), malloc(cython.sizeof(my_c_struct))) + _ptr: cython.pointer[my_c_struct] = cython.cast( + cython.pointer[my_c_struct], malloc(cython.sizeof(my_c_struct))) if _ptr is cython.NULL: raise MemoryError _ptr.a = 0 diff --git a/docs/examples/userguide/fusedtypes/pointer.py b/docs/examples/userguide/fusedtypes/pointer.py index 043074c794d..fa2f6040705 100644 --- a/docs/examples/userguide/fusedtypes/pointer.py +++ b/docs/examples/userguide/fusedtypes/pointer.py @@ -2,7 +2,7 @@ @cython.cfunc -def func(a: cython.pointer(my_fused_type)): +def func(a: cython.pointer[my_fused_type]): print(a[0]) def main(): diff --git a/docs/examples/userguide/language_basics/casting_python.py b/docs/examples/userguide/language_basics/casting_python.py index 1c02c461cd4..3f13b91c7c6 100644 --- a/docs/examples/userguide/language_basics/casting_python.py +++ b/docs/examples/userguide/language_basics/casting_python.py @@ -12,7 +12,7 @@ def main(): adress_in_c = cython.cast(Py_intptr_t, ptr) address_from_void = adress_in_c # address_from_void is a python int - ptr2 = cython.cast(cython.pointer(PyObject), python_string) + ptr2 = cython.cast(cython.pointer[PyObject], python_string) address_in_c2 = cython.cast(Py_intptr_t, ptr2) address_from_PyObject = address_in_c2 # address_from_PyObject is a python int diff --git a/docs/examples/userguide/language_basics/parameter_refcount.py b/docs/examples/userguide/language_basics/parameter_refcount.py index 0c5183da455..b2b68e55832 100644 --- a/docs/examples/userguide/language_basics/parameter_refcount.py +++ b/docs/examples/userguide/language_basics/parameter_refcount.py @@ -16,7 +16,7 @@ def owned_reference(obj: object): print(f'Inside owned_reference after new ref: {refcount2}') @cython.cfunc -def borrowed_reference(obj: cython.pointer(PyObject)): +def borrowed_reference(obj: cython.pointer[PyObject]): refcount1 = _Py_REFCNT(obj) print(f'Inside borrowed_reference initially: {refcount1}') another_ptr_to_object = obj @@ -31,4 +31,4 @@ def borrowed_reference(obj: cython.pointer(PyObject)): print(f'Initial refcount: {python_dict_refcount}') owned_reference(python_dict) -borrowed_reference(cython.cast(cython.pointer(PyObject), python_dict)) +borrowed_reference(cython.cast(cython.pointer[PyObject], python_dict)) diff --git a/docs/examples/userguide/language_basics/type_qualifiers.py b/docs/examples/userguide/language_basics/type_qualifiers.py new file mode 100644 index 00000000000..a427688c916 --- /dev/null +++ b/docs/examples/userguide/language_basics/type_qualifiers.py @@ -0,0 +1,29 @@ +def use_volatile(): + i: cython.volatile[cython.int] = 5 + +@cython.cfunc +def sum(a: cython.const[cython.int], b: cython.const[cython.int]) -> cython.const[cython.int]: + return a + b + +@cython.cfunc +def pointer_to_const_int(value: cython.pointer[cython.const[cython.int]]) -> cython.void: + # Declares value as pointer to const int type (alias: "cython.p_const_int"). + # The value can be modified but the object pointed to by value cannot be modified. + new_value: cython.int = 10 + print(value[0]) + value = cython.address(new_value) + print(value[0]) + +@cython.cfunc +def const_pointer_to_int(value: cython.const[cython.pointer[cython.int]]) -> cython.void: + # Declares value as const pointer to int type (alias: "cython.const[cython.p_int]"). + # Value cannot be modified but the object pointed to by value can be modified. + print(value[0]) + value[0] = 10 + print(value[0]) + +@cython.cfunc +def const_pointer_to_const_int(value: cython.const[cython.pointer[cython.const[cython.int]]]) -> cython.void: + # Declares value as const pointer to const int type (alias: "cython.const[cython.p_const_int]"). + # Neither the value variable nor the int pointed to can be modified. + print(value[0]) diff --git a/docs/examples/userguide/language_basics/type_qualifiers.pyx b/docs/examples/userguide/language_basics/type_qualifiers.pyx new file mode 100644 index 00000000000..a90e3302749 --- /dev/null +++ b/docs/examples/userguide/language_basics/type_qualifiers.pyx @@ -0,0 +1,29 @@ +def use_volatile(): + cdef volatile int i = 5 + + +cdef const int sum(const int a, const int b): + return a + b + + +cdef void pointer_to_const_int(const int *value): + # Declares value as pointer to const int type. + # The value can be modified but the object pointed to by value cannot be modified. + cdef int new_value = 10 + print(value[0]) + value = &new_value + print(value[0]) + + +cdef void const_pointer_to_int(int * const value): + # Declares value as const pointer to int type. + # Value cannot be modified but the object pointed to by value can be modified. + print(value[0]) + value[0] = 10 + print(value[0]) + + +cdef void const_pointer_to_const_int(const int * const value): + # Declares value as const pointer to const int type. + # Neither the value variable nor the int pointed to can be modified. + print(value[0]) diff --git a/docs/examples/userguide/sharing_declarations/restaurant.py b/docs/examples/userguide/sharing_declarations/restaurant.py index b4bdb2eba66..8696c5917a2 100644 --- a/docs/examples/userguide/sharing_declarations/restaurant.py +++ b/docs/examples/userguide/sharing_declarations/restaurant.py @@ -2,7 +2,7 @@ from cython.cimports.dishes import spamdish, sausage @cython.cfunc -def prepare(d: cython.pointer(spamdish)) -> cython.void: +def prepare(d: cython.pointer[spamdish]) -> cython.void: d.oz_of_spam = 42 d.filler = sausage diff --git a/docs/src/tutorial/annotation_typing_table.csv b/docs/src/tutorial/annotation_typing_table.csv index 43c48b1ab73..aed0b42e3be 100644 --- a/docs/src/tutorial/annotation_typing_table.csv +++ b/docs/src/tutorial/annotation_typing_table.csv @@ -5,5 +5,7 @@ Feature ,Cython 0.29 ,Cython 3.0 Extension type defined in Cython ,,"Specified type or a subclasses, not ``None``" "``cython.int``, ``cython.long``, etc. ",,Equivalent C numeric type ``typing.Optional[any_type]``,Not supported,"Specified type (which must be a Python object), allows ``None``" +"``typing.Union[any_type, None]``",Not supported,"Specified type (which must be a Python object), allows ``None``" +``any_type | None``,Not supported,"Specified type (which must be a Python object), allows ``None``" ``typing.List[any_type]`` (and similar) ,Not supported,"Exact ``list``, with the element type ignored currently " ``typing.ClassVar[...]`` ,Not supported,Python-object class variable (when used in a class definition) diff --git a/docs/src/tutorial/clibraries.rst b/docs/src/tutorial/clibraries.rst index 47755f2e1a4..9669458a8fa 100644 --- a/docs/src/tutorial/clibraries.rst +++ b/docs/src/tutorial/clibraries.rst @@ -273,7 +273,7 @@ To build the c-code automatically we need to include compiler directives in :fil @cython.cclass class Queue: - _c_queue = cython.declare(cython.pointer(cqueue.Queue)) + _c_queue = cython.declare(cython.pointer[cqueue.Queue]) def __cinit__(self): self._c_queue = cqueue.queue_new() diff --git a/docs/src/tutorial/pure.rst b/docs/src/tutorial/pure.rst index f36b9530dbd..49bc2a04b49 100644 --- a/docs/src/tutorial/pure.rst +++ b/docs/src/tutorial/pure.rst @@ -170,7 +170,8 @@ as well as their unsigned versions ``uchar``, ``ushort``, ``uint``, ``ulong``, For each type, there are pointer types ``p_int``, ``pp_int``, etc., up to three levels deep in interpreted mode, and infinitely deep in compiled mode. -Further pointer types can be constructed with ``cython.pointer(cython.int)``, +Further pointer types can be constructed with ``cython.pointer[cython.int]`` +(or ``cython.pointer(cython.int)`` for compatibility with Cython versions before 3.1), and arrays as ``cython.int[10]``. A limited attempt is made to emulate these more complex types, but only so much can be done from the Python language. @@ -222,9 +223,9 @@ Managing the Global Interpreter Lock @cython.cfunc def func_released_gil() -> cython.int: # function that can be run with the GIL released - + Note that the two uses differ: the context manager releases the GIL while the decorator marks that a - function *can* be run without the GIL. See :ref:`` for more details. + function *can* be run without the GIL. See :ref:`cython_and_gil` for more details. * ``cython.gil`` can be used as a context manager to replace the :keyword:`gil` keyword:: diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index 67011bb45b8..d183e03f0c3 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -373,6 +373,7 @@ you need to explicitly allow ``None`` values for them. 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. + * ``typing.Union[tp, None]`` and ``tp | None`` can be used as alternatives to ``typing.Optional`` Special methods ================ @@ -921,7 +922,7 @@ the ``no_gc_clear`` directive: @cython.cclass class DBCursor: conn: DBConnection - raw_cursor: cython.pointer(DBAPI_Cursor) + raw_cursor: cython.pointer[DBAPI_Cursor] # ... def __dealloc__(self): DBAPI_close_cursor(self.conn.raw_conn, self.raw_cursor) diff --git a/docs/src/userguide/fusedtypes.rst b/docs/src/userguide/fusedtypes.rst index 3bff3e33fe8..d114cd7107c 100644 --- a/docs/src/userguide/fusedtypes.rst +++ b/docs/src/userguide/fusedtypes.rst @@ -223,7 +223,7 @@ and typed views of memory however. Indeed, one may write: # and @cython.cfunc - cdef otherfunc(x: cython.pointer(A)): + cdef otherfunc(x: cython.pointer[A]): ... @@ -345,7 +345,7 @@ not the full argument type: .. code-block:: python @cython.cfunc - def myfunc(x: cython.pointer(A)): + def myfunc(x: cython.pointer[A]): ... # Specialize using int, not int * diff --git a/docs/src/userguide/language_basics.rst b/docs/src/userguide/language_basics.rst index ee1729a3a46..1aa9b7b5c78 100644 --- a/docs/src/userguide/language_basics.rst +++ b/docs/src/userguide/language_basics.rst @@ -169,7 +169,7 @@ C array can be declared by adding ``[ARRAY_SIZE]`` to the type of variable: def func(): g: cython.float[42] f: cython.int[5][5][5] - ptr_char_array: cython.pointer(cython.char[4]) # pointer to the array of 4 chars + ptr_char_array: cython.pointer[cython.char[4]] # pointer to the array of 4 chars array_ptr_char: cython.p_char[4] # array of 4 char pointers .. group-tab:: Cython @@ -360,8 +360,8 @@ containers. Pointer types are constructed as in C when using Cython syntax, by appending a ``*`` to the base type they point to, e.g. ``int**`` for a pointer to a pointer to a C int. In Pure python mode, simple pointer types use a naming scheme with "p"s instead, separated from the type name with an underscore, e.g. ``cython.pp_int`` for a pointer to -a pointer to a C int. Further pointer types can be constructed with the ``cython.pointer()`` function, -e.g. ``cython.pointer(cython.int)``. +a pointer to a C int. Further pointer types can be constructed with the ``cython.pointer[]`` type construct, +e.g. ``cython.p_int`` is equivalent to ``cython.pointer[cython.int]``. Arrays use the normal C array syntax, e.g. ``int[10]``, and the size must be known at compile time for stack allocated arrays. Cython doesn't support variable length arrays from C99. @@ -433,25 +433,53 @@ type declaration and let them be objects. Type qualifiers --------------- -Cython supports ``const`` and ``volatile`` `C type qualifiers `_:: +Cython supports ``const`` and ``volatile`` `C type qualifiers `_ - cdef volatile int i = 5 +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/language_basics/type_qualifiers.py + + + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/language_basics/type_qualifiers.pyx + + +Similar to pointers Cython supports shortcut types that can be used in pure python mode. The following table shows several examples: + +.. list-table:: Type qualifiers shortcut types + :widths: 100 10 10 + :header-rows: 1 + + * - Cython + - Full Annotation type + - Shortcut type + * - ``const float`` + - ``cython.const[cython.float]`` + - ``cython.const_float`` + + * - ``const void *`` + - ``cython.pointer[cython.const[cython.void]]`` + - ``cython.p_const_void`` + + * - ``const int *`` + - ``cython.pointer[cython.const[cython.int]]`` + - ``cython.p_const_int`` - cdef const int sum(const int a, const int b): - return a + b + * - ``const long **`` + - ``cython.pointer[cython.pointer[cython.const[cython.long]]]`` + - ``cython.pp_const_long`` - cdef void print_const_pointer(const int *value): - print(value[0]) - cdef void print_pointer_to_const_value(int * const value): - print(value[0]) +For full list of shortcut types see the ``Shadow.pyi`` file. - cdef void print_const_pointer_to_const_value(const int * const value): - print(value[0]) .. Note:: - Both type qualifiers are not supported by pure python mode. Moreover, the ``const`` modifier is unusable + The ``const`` modifier is unusable in a lot of contexts since Cython needs to generate definitions and their assignments separately. Therefore we suggest using it mainly for function argument and pointer types where ``const`` is necessary to work with an existing C/C++ interface. @@ -1215,7 +1243,7 @@ Cython uses ``"<"`` and ``">"``. In pure python mode, the ``cython.cast()`` fun .. note:: Cython will not prevent a redundant cast, but emits a warning for it. To get the address of some Python object, use a cast to a pointer type - like ``cast(p_void, ...)`` or ``cast(pointer(PyObject), ...)``. + like ``cast(p_void, ...)`` or ``cast(pointer[PyObject], ...)``. You can also cast a C pointer back to a Python object reference with ``cast(object, ...)``, or to a more specific builtin or extension type (e.g. ``cast(MyExtType, ptr)``). This will increase the reference count of @@ -1257,7 +1285,7 @@ Cython uses ``"<"`` and ``">"``. In pure python mode, the ``cython.cast()`` fun Casting with ``cast(object, ...)`` creates an owned reference. Cython will automatically perform a :c:func:`Py_INCREF` and :c:func:`Py_DECREF` operation. Casting to - ``cast(pointer(PyObject), ...)`` creates a borrowed reference, leaving the refcount unchanged. + ``cast(pointer[PyObject], ...)`` creates a borrowed reference, leaving the refcount unchanged. .. group-tab:: Cython diff --git a/runtests.py b/runtests.py index 9166c5520e8..054ce605936 100755 --- a/runtests.py +++ b/runtests.py @@ -1045,10 +1045,7 @@ def setUp(self): # 'cache_builtins', # not currently supported due to incorrect global caching ) ] - self._saved_default_directives = list(Options.get_directive_defaults().items()) Options.warning_errors = self.warning_errors - Options._directive_defaults['autotestdict'] = False - Options._directive_defaults.update(self.extra_directives) if not os.path.exists(self.workdir): os.makedirs(self.workdir) @@ -1068,7 +1065,6 @@ def tearDown(self): from Cython.Compiler import Options for name, value in self._saved_options: setattr(Options, name, value) - Options._directive_defaults = dict(self._saved_default_directives) unpatch_inspect_isfunction() try: @@ -1244,6 +1240,10 @@ def run_cython(self, test_directory, module, module_path, targetdir, incdir, ann from Cython.Compiler.Main import compile as cython_compile common_utility_include_dir = self.common_utility_dir + compiler_directives = { + 'autotestdict': False, + **self.extra_directives, + } options = CompilationOptions( pyrex_default_options, include_path = include_dirs, @@ -1257,6 +1257,7 @@ def run_cython(self, test_directory, module, module_path, targetdir, incdir, ann evaluate_tree_assertions = True, common_utility_include_dir = common_utility_include_dir, c_line_in_traceback = True, + compiler_directives = compiler_directives, **extra_compile_options ) cython_compile(module_path, options=options, full_module_name=module) @@ -1683,8 +1684,8 @@ def run(self, result=None): with self.stats.time(self.name, 'py', 'mypy'): mypy_result = mypy_api.run([ self.module_path, - '--ignore-missing-imports', - '--follow-imports', 'skip', + #'--ignore-missing-imports', + #'--follow-imports', 'skip', '--python-version', '3.10', ]) if mypy_result[2]: diff --git a/tests/compile/inplace_lhs_nested.pyx b/tests/compile/inplace_lhs_nested.pyx new file mode 100644 index 00000000000..8149bc46fe1 --- /dev/null +++ b/tests/compile/inplace_lhs_nested.pyx @@ -0,0 +1,17 @@ +# mode: compile + +cdef class A: + cdef int b + +cdef class C: + cdef A _a + + +cdef void set_a(A a) noexcept nogil: + a.b |= 1 + +cdef void set_b(C c) noexcept nogil: + c._a.b |= 1 + +cdef void set_c(C c) noexcept nogil: + c._a.b = c._a.b | 1 diff --git a/tests/compile/profile_exttypereturn.pyx b/tests/compile/profile_exttypereturn.pyx new file mode 100644 index 00000000000..50f134a3334 --- /dev/null +++ b/tests/compile/profile_exttypereturn.pyx @@ -0,0 +1,12 @@ +# mode: compile +# cython: profile=True + +cdef class Foo: + ... + +cdef Foo _foo(): + return Foo() + +def foo(): + return _foo() + diff --git a/tests/errors/e_typing_errors.pyx b/tests/errors/e_typing_errors.pyx index df0dc2e39ac..ca6bda6d1fc 100644 --- a/tests/errors/e_typing_errors.pyx +++ b/tests/errors/e_typing_errors.pyx @@ -30,6 +30,9 @@ cdef ClassVar[list] x def union_pytypes(Union[int, None] i, Union[None, float] f, Union[complex, None] c, Union[long, None] l): pass +def bitwise_or_pytypes(i: cython.int | None, f: None | cython.float , c: cython.complex | None, l: cython.long | None): + pass + # OK def optional_memoryview(double[:] d, Optional[double[:]] o): @@ -38,6 +41,9 @@ def optional_memoryview(double[:] d, Optional[double[:]] o): def union_memoryview(double[:] d, Union[double[:], None] o): pass +def bitwise_or_not_recognized_type(x: DummyType | None, y: None | DummyType): + pass + cdef class Cls(object): cdef ClassVar[list] x @@ -61,4 +67,9 @@ _ERRORS = """ 30:29: typing.Union[...] cannot be applied to type int 30:50: typing.Union[...] cannot be applied to type float 30:96: typing.Union[...] cannot be applied to type long + +33:32: '[...] | None' cannot be applied to type int +33:61: '[...] | None' cannot be applied to type float +33:79: '[...] | None' cannot be applied to type double complex +33:105: '[...] | None' cannot be applied to type long """ diff --git a/tests/memoryview/memoryview_annotation_typing.py b/tests/memoryview/memoryview_annotation_typing.py index 53a50caf281..57c04539510 100644 --- a/tests/memoryview/memoryview_annotation_typing.py +++ b/tests/memoryview/memoryview_annotation_typing.py @@ -111,6 +111,31 @@ def slice_union(m: typing.Union[cython.double[:], None]): """ return 1 if m is None else 2 +def slice_bitwise_or_none(m: cython.double[:] | None, n: None | cython.double[:]): + """ + >>> slice_bitwise_or_none(None, None) + (1, 1) + >>> a = numpy.ones((10,), numpy.double) + >>> slice_bitwise_or_none(a, a) + (2, 2) + + # Make sure that we actually evaluate the type and don't just accept everything. + >>> try: + ... x = slice_bitwise_or_none(123, None) + ... except TypeError as exc: + ... if not COMPILED: raise + ... else: + ... assert not COMPILED + + >>> try: + ... x = slice_bitwise_or_none(None, 123) + ... except TypeError as exc: + ... if not COMPILED: raise + ... else: + ... assert not COMPILED + """ + return (1 if m is None else 2, 1 if n is None else 2) + @cython.nogil @cython.cfunc def _one_dim_nogil_cfunc(a: cython.double[:]) -> cython.double: diff --git a/tests/run/annotation_typing.pyx b/tests/run/annotation_typing.pyx index a5b00072fca..5ea699debe3 100644 --- a/tests/run/annotation_typing.pyx +++ b/tests/run/annotation_typing.pyx @@ -34,23 +34,26 @@ def old_dict_syntax(a: list, b: "int" = 2, c: {'ctype': 'long int'} = 3, d: {'ty return a -def pytypes_def(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = (), v: Union[tuple, None]=()) -> list: +def pytypes_def(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = (), v: Union[tuple, None]=(), u: tuple | None = ()) -> list: """ >>> pytypes_def([1]) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 2, 3, 4.0, None, (), ()] + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 2, 3, 4.0, None, (), (), ()] >>> pytypes_def([1], 3) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 3, 3, 4.0, None, (), ()] - >>> pytypes_def([1], 3, 2, 1, [], None, None) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 3, 2, 1.0, [], None, None] - >>> pytypes_def([1], 3, 2, 1, [], 'a', ()) + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 3, 3, 4.0, None, (), (), ()] + >>> pytypes_def([1], 3, 2, 1, [], None, None, None) + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 3, 2, 1.0, [], None, None, None] + >>> pytypes_def([1], 3, 2, 1, [], 'a', (), ()) Traceback (most recent call last): TypeError: Argument 'o' has incorrect type (expected tuple, got str) >>> pytypes_def([1], 3, 2, 1, [], (), 'a') Traceback (most recent call last): TypeError: Argument 'v' has incorrect type (expected tuple, got str) + >>> pytypes_def([1], 3, 2, 1, [], (), (), 'a') + Traceback (most recent call last): + TypeError: Argument 'u' has incorrect type (expected tuple, got str) >>> pytypes_def(123) Traceback (most recent call last): TypeError: Argument 'a' has incorrect type (expected list, got int) @@ -58,33 +61,37 @@ def pytypes_def(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None Traceback (most recent call last): TypeError: Argument 'a' has incorrect type (expected list, got NoneType) """ - print((typeof(a), typeof(b), typeof(c), typeof(d), typeof(n), typeof(o), typeof(v))) + print((typeof(a), typeof(b), typeof(c), typeof(d), typeof(n), typeof(o), typeof(v), typeof(u))) a.append(b) a.append(c) a.append(d) a.append(n) a.append(o) a.append(v) + a.append(u) return a -cpdef pytypes_cpdef(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = (), v: Union[tuple, None]=()): +cpdef pytypes_cpdef(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = (), v: Union[tuple, None]=(), u: tuple | None = ()): """ >>> pytypes_cpdef([1]) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 2, 3, 4.0, None, (), ()] + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 2, 3, 4.0, None, (), (), ()] >>> pytypes_cpdef([1], 3) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 3, 3, 4.0, None, (), ()] - >>> pytypes_cpdef([1], 3, 2, 1, [], None, None) - ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object') - [1, 3, 2, 1.0, [], None, None] - >>> pytypes_cpdef([1], 3, 2, 1, [], 'a', ()) + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 3, 3, 4.0, None, (), (), ()] + >>> pytypes_cpdef([1], 3, 2, 1, [], None, None, None) + ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object', 'tuple object', 'tuple object') + [1, 3, 2, 1.0, [], None, None, None] + >>> pytypes_cpdef([1], 3, 2, 1, [], 'a', (), ()) Traceback (most recent call last): TypeError: Argument 'o' has incorrect type (expected tuple, got str) - >>> pytypes_cpdef([1], 3, 2, 1, [], (), 'a') + >>> pytypes_cpdef([1], 3, 2, 1, [], (), 'a', ()) Traceback (most recent call last): TypeError: Argument 'v' has incorrect type (expected tuple, got str) + >>> pytypes_cpdef([1], 3, 2, 1, [], (), (), 'a') + Traceback (most recent call last): + TypeError: Argument 'u' has incorrect type (expected tuple, got str) >>> pytypes_cpdef(123) Traceback (most recent call last): TypeError: Argument 'a' has incorrect type (expected list, got int) @@ -92,13 +99,14 @@ cpdef pytypes_cpdef(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = Traceback (most recent call last): TypeError: Argument 'a' has incorrect type (expected list, got NoneType) """ - print((typeof(a), typeof(b), typeof(c), typeof(d), typeof(n), typeof(o), typeof(v))) + print((typeof(a), typeof(b), typeof(c), typeof(d), typeof(n), typeof(o), typeof(v), typeof(u))) a.append(b) a.append(c) a.append(d) a.append(n) a.append(o) a.append(v) + a.append(u) return a @@ -311,27 +319,27 @@ class LateClass(object): pass -def py_float_default(price : Optional[float]=None, bar: Union[float, None]=None, ndigits=4): +def py_float_default(price : Optional[float]=None, bar: Union[float, None]=None, spam: float | None = None, ndigits=4): """ Python default arguments should prevent C type inference. >>> py_float_default() - (None, None, 4) + (None, None, None, 4) >>> py_float_default(None) - (None, None, 4) + (None, None, None, 4) >>> py_float_default(2) # doctest: +ELLIPSIS Traceback (most recent call last): TypeError: ...float... - >>> py_float_default(2.0, 3.0) - (2.0, 3.0, 4) - >>> py_float_default(2, None, 4) # doctest: +ELLIPSIS + >>> py_float_default(2.0, 3.0, 4.0) + (2.0, 3.0, 4.0, 4) + >>> py_float_default(2, None, None, 4) # doctest: +ELLIPSIS Traceback (most recent call last): TypeError: ...float... - >>> py_float_default(None, 2, 4) # doctest: +ELLIPSIS + >>> py_float_default(None, 2, None, 4) # doctest: +ELLIPSIS Traceback (most recent call last): TypeError: ...float... """ - return price, bar, ndigits + return price, bar, spam, ndigits cdef class ClassAttribute: @@ -426,6 +434,21 @@ def test_inexact_types(d: dict): pass +def pytypes_multi_union(a: Union[list, tuple, None], b: list | tuple | None): + """ + >>> pytypes_multi_union([1], [2]) + ('Python object', 'Python object') + [[1], [2]] + >>> pytypes_multi_union((1,), (2,)) + ('Python object', 'Python object') + [(1,), (2,)] + >>> pytypes_multi_union(1, 2) + ('Python object', 'Python object') + [1, 2] + """ + print((typeof(a), typeof(b))) + return [a, b] + _WARNINGS = """ 15:32: Strings should no longer be used for type declarations. Use 'cython.int' etc. directly. 15:47: Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly. @@ -436,24 +459,26 @@ _WARNINGS = """ 37:40: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? 37:40: Unknown type declaration 'long' in annotation, ignoring 37:66: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None. -71:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? -71:44: Unknown type declaration 'long' in annotation, ignoring -71:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None. -105:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? -105:44: Unknown type declaration 'long' in annotation, ignoring -105:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None. -167:30: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. -167:59: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. -172:13: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. -307:44: Unknown type declaration in annotation, ignoring -338:15: Annotation ignored since class-level attributes must be Python objects. Were you trying to set up an instance attribute? +75:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? +75:44: Unknown type declaration 'long' in annotation, ignoring +75:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None. +113:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? +113:44: Unknown type declaration 'long' in annotation, ignoring +113:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None. +175:30: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. +175:59: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. +180:13: Tuples cannot be declared as simple tuples of types. Use 'tuple[type1, type2, ...]'. +315:44: Unknown type declaration in annotation, ignoring +346:15: Annotation ignored since class-level attributes must be Python objects. Were you trying to set up an instance attribute? +437:32: Unknown type declaration in annotation, ignoring +437:69: Unknown type declaration in annotation, ignoring # DUPLICATE: -71:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? -71:44: Unknown type declaration 'long' in annotation, ignoring +75:44: Found C type name 'long' in a Python annotation. Did you mean to use 'cython.long'? +75:44: Unknown type declaration 'long' in annotation, ignoring # BUG: -71:0: 'pytypes_cpdef' redeclared -179:0: 'struct_io' redeclared -214:0: 'struct_convert' redeclared -233:0: 'exception_default' redeclared -264:0: 'exception_default_uint' redeclared +75:0: 'pytypes_cpdef' redeclared +187:0: 'struct_io' redeclared +222:0: 'struct_convert' redeclared +241:0: 'exception_default' redeclared +272:0: 'exception_default_uint' redeclared """ diff --git a/tests/run/cstruct.pyx b/tests/run/cstruct.pyx index c48665586d9..d16ac35831c 100644 --- a/tests/run/cstruct.pyx +++ b/tests/run/cstruct.pyx @@ -1,14 +1,26 @@ +# mode: run +#tag: struct + cdef struct Grail + cdef struct Spam: int i char c float *p[42] Grail *g + cdef struct Grail: Spam *s + +cdef struct ReservedNames: + int new + int case + int do + + cdef Spam spam, ham cdef void eggs_i(Spam s): @@ -81,3 +93,15 @@ def assign_fields_in_loop(): assert s.a == s.b return s.b + + +def reserved_names(): + """ + >>> reserved_names() + (2, 5, 9) + """ + cdef ReservedNames s1 = ReservedNames(new=2, case=5, do=9) + cdef ReservedNames s2 + s2.new = s1.new + s2.case, s2.do = s1.case, s1.do + return (s2.new, s2.case, s2.do) diff --git a/tests/run/ext_type_none_arg.pyx b/tests/run/ext_type_none_arg.pyx index 4507372648c..fcdffb2f184 100644 --- a/tests/run/ext_type_none_arg.pyx +++ b/tests/run/ext_type_none_arg.pyx @@ -142,6 +142,26 @@ def ext_union(x: typing.Union[MyExtType, None], y: Union[None, MyExtType]): """ return attr(x) + attr(y) + +def ext_bitwise_or_none(x: MyExtType | None, y: None | MyExtType): + """ + Behaves the same as "or None" + >>> ext_bitwise_or_none(MyExtType(), MyExtType()) + 246 + >>> ext_bitwise_or_none(MyExtType(), None) + 444 + >>> ext_bitwise_or_none(None, MyExtType()) + 444 + >>> ext_bitwise_or_none([], MyExtType()) + Traceback (most recent call last): + TypeError: Argument 'x' has incorrect type (expected ext_type_none_arg.MyExtType, got list) + >>> ext_bitwise_or_none(MyExtType(), []) + Traceback (most recent call last): + TypeError: Argument 'y' has incorrect type (expected ext_type_none_arg.MyExtType, got list) + """ + return attr(x) + attr(y) + + ### builtin types (using list) cdef litem(list L, int item): diff --git a/tests/run/legacy_implicit_noexcept_build.srctree b/tests/run/legacy_implicit_noexcept_build.srctree index 2928db625b9..2667e9deabf 100644 --- a/tests/run/legacy_implicit_noexcept_build.srctree +++ b/tests/run/legacy_implicit_noexcept_build.srctree @@ -179,10 +179,6 @@ try: aa.r3(True) except ValueError: assert False, "ValueError not raised" else: pass -######## bar.pxd ######## -cdef int func_noexcept_declared_in_pxd() noexcept -cdef int func_implicit_declared_in_pxd() - ######## bar.pyx ######## cdef int func_noexcept() noexcept: @@ -191,21 +187,12 @@ cdef int func_noexcept() noexcept: cdef int func_implicit(): raise RuntimeError() -cdef int func_noexcept_declared_in_pxd(): - raise RuntimeError() - -cdef int func_implicit_declared_in_pxd(): - raise RuntimeError() - cdef int func_return_value() except -1: raise RuntimeError() func_noexcept() func_implicit() -func_noexcept_declared_in_pxd() -func_implicit_declared_in_pxd() - try: func_return_value() except RuntimeError: diff --git a/tests/run/pep526_variable_annotations.py b/tests/run/pep526_variable_annotations.py index 920151d5406..0c1a78861c2 100644 --- a/tests/run/pep526_variable_annotations.py +++ b/tests/run/pep526_variable_annotations.py @@ -30,6 +30,7 @@ t2: tuple[cython.int, ...] body: Optional[List[str]] body2: Union[List[str], None] +body3: List[str] | None descr_only : "descriptions are allowed but ignored" @@ -51,6 +52,7 @@ def f(): t: Tuple[cython.int, ...] = (1, 2, 3) body: Optional[List[str]] body2: Union[None, List[str]] + body3: None | List[str] descr_only: "descriptions are allowed but ignored" return var, fvar, some_list, t @@ -193,75 +195,6 @@ def test_subscripted_types(): print(cython.typeof(b3) + (" object" if not cython.compiled else "")) print(cython.typeof(c) + (" object" if not cython.compiled else "")) -# because tuple is specifically special cased to go to ctuple where possible -def test_tuple(a: typing.Tuple[cython.int, cython.float], b: typing.Tuple[cython.int, ...], - c: Tuple[cython.int, object] # cannot be a ctuple - ): - """ - >>> test_tuple((1, 1.0), (1, 1.0), (1, 1.0)) - int - int - float - Python object - (int, float) - tuple object - tuple object - tuple object - tuple object - """ - x: typing.Tuple[int, float] = (a[0], a[1]) # note: Python int/float, not cython.int/float - y: Tuple[cython.int, ...] = (1,2.) - plain_tuple: Tuple = () - z = a[0] # should infer to C int - p = x[1] # should infer to Python float -> C double - - print(cython.typeof(z)) - print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0])) # FIXME: infer Python int - if cython.compiled: - print(cython.typeof(p)) - else: - print('float' if cython.typeof(p) == 'float' else cython.typeof(p)) - print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object") # FIXME: infer C double - print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)") - print(cython.typeof(x) + (" object" if not cython.compiled else "")) - print(cython.typeof(y) + (" object" if not cython.compiled else "")) - print(cython.typeof(c) + (" object" if not cython.compiled else "")) - print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else "")) - - -# because tuple is specifically special cased to go to ctuple where possible -def test_tuple_without_typing(a: tuple[cython.int, cython.float], b: tuple[cython.int, ...], - c: tuple[cython.int, object] # cannot be a ctuple - ): - """ - >>> test_tuple_without_typing((1, 1.0), (1, 1.0), (1, 1.0)) - int - int - float - Python object - (int, float) - tuple object - tuple object - tuple object - tuple object - """ - x: tuple[int, float] = (a[0], a[1]) # note: Python int/float, not cython.int/float - y: tuple[cython.int, ...] = (1,2.) - plain_tuple: tuple = () - z = a[0] # should infer to C int - p = x[1] # should infer to Python float -> C double - - print(cython.typeof(z)) - print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0])) # FIXME: infer Python int - print(cython.typeof(p) if cython.compiled or cython.typeof(p) != 'float' else "float") # FIXME: infer C double/PyFloat from Py type - print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object") # FIXME: infer C double - print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)") - print(cython.typeof(x) + (" object" if not cython.compiled else "")) - print(cython.typeof(y) + (" object" if not cython.compiled else "")) - print(cython.typeof(c) + (" object" if not cython.compiled else "")) - print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else "")) - - def test_use_typing_attributes_as_non_annotations(): """ >>> test_use_typing_attributes_as_non_annotations() @@ -299,22 +232,6 @@ def test_use_typing_attributes_as_non_annotations(): print(q1, str(q2) == "typing.Union[typing.FrozenSet, NoneType]" or str(q2)) print(w1, str(w2) == "typing.Union[typing.Dict, NoneType]" or str(w2)) -def test_optional_ctuple(x: typing.Optional[tuple[float]]): - """ - Should not be a C-tuple (because these can't be optional) - >>> test_optional_ctuple((1.0,)) - tuple object - """ - print(cython.typeof(x) + (" object" if not cython.compiled else "")) - -def test_union_ctuple(x: typing.Union[tuple[float], None]): - """ - Should not be a C-tuple (because these can't be optional) - >>> test_union_ctuple((1.0,)) - tuple object - """ - print(cython.typeof(x) + (" object" if not cython.compiled else "")) - try: import numpy.typing as npt import numpy as np diff --git a/tests/run/pure_ctuple.py b/tests/run/pure_ctuple.py new file mode 100644 index 00000000000..33a66e64b21 --- /dev/null +++ b/tests/run/pure_ctuple.py @@ -0,0 +1,113 @@ +# cython: language_level=3 +# mode: run +# tag: pure3.7, pep484, warnings + +# for the benefit of the pure tests, don't require annotations +# to be evaluated +from __future__ import annotations + +import typing +from typing import Tuple +import cython + + +def test_optional_ctuple(x: typing.Optional[tuple[float]]): + """ + Should not be a C-tuple (because these can't be optional) + >>> test_optional_ctuple((1.0,)) + tuple object + """ + print(cython.typeof(x) + (" object" if not cython.compiled else "")) + + +def test_union_ctuple(x: typing.Union[tuple[float], None]): + """ + Should not be a C-tuple (because these can't be optional) + >>> test_union_ctuple((1.0,)) + tuple object + """ + print(cython.typeof(x) + (" object" if not cython.compiled else "")) + + +def test_bitwise_or_ctuple(x: tuple[float] | None, y: None | tuple[float]): + """ + Should not be a C-tuple (because these can't be optional) + >>> test_bitwise_or_ctuple((1.0,), (2.0,)) + tuple object + tuple object + """ + print(cython.typeof(x) + (" object" if not cython.compiled else "")) + print(cython.typeof(y) + (" object" if not cython.compiled else "")) + + +# because tuple is specifically special cased to go to ctuple where possible +def test_tuple(a: typing.Tuple[cython.int, cython.float], b: typing.Tuple[cython.int, ...], + c: Tuple[cython.int, object] # cannot be a ctuple + ): + """ + >>> test_tuple((1, 1.0), (1, 1.0), (1, 1.0)) + int + int + float + Python object + (int, float) + tuple object + tuple object + tuple object + tuple object + """ + x: typing.Tuple[int, float] = (a[0], a[1]) # note: Python int/float, not cython.int/float + y: Tuple[cython.int, ...] = (1,2.) + plain_tuple: Tuple = () + z = a[0] # should infer to C int + p = x[1] # should infer to Python float -> C double + + print(cython.typeof(z)) + print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0])) # FIXME: infer Python int + if cython.compiled: + print(cython.typeof(p)) + else: + print('float' if cython.typeof(p) == 'float' else cython.typeof(p)) + print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object") # FIXME: infer C double + print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)") + print(cython.typeof(x) + (" object" if not cython.compiled else "")) + print(cython.typeof(y) + (" object" if not cython.compiled else "")) + print(cython.typeof(c) + (" object" if not cython.compiled else "")) + print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else "")) + + +# because tuple is specifically special cased to go to ctuple where possible +def test_tuple_without_typing(a: tuple[cython.int, cython.float], b: tuple[cython.int, ...], + c: tuple[cython.int, object] # cannot be a ctuple + ): + """ + >>> test_tuple_without_typing((1, 1.0), (1, 1.0), (1, 1.0)) + int + int + float + Python object + (int, float) + tuple object + tuple object + tuple object + tuple object + """ + x: tuple[int, float] = (a[0], a[1]) # note: Python int/float, not cython.int/float + y: tuple[cython.int, ...] = (1,2.) + plain_tuple: tuple = () + z = a[0] # should infer to C int + p = x[1] # should infer to Python float -> C double + + print(cython.typeof(z)) + print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0])) # FIXME: infer Python int + print(cython.typeof(p) if cython.compiled or cython.typeof(p) != 'float' else "float") # FIXME: infer C double/PyFloat from Py type + print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object") # FIXME: infer C double + print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)") + print(cython.typeof(x) + (" object" if not cython.compiled else "")) + print(cython.typeof(y) + (" object" if not cython.compiled else "")) + print(cython.typeof(c) + (" object" if not cython.compiled else "")) + print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else "")) + + +_WARNINGS = """ +""" diff --git a/tests/run/pure_py.py b/tests/run/pure_py.py index 73b87f30279..48b52af3c51 100644 --- a/tests/run/pure_py.py +++ b/tests/run/pure_py.py @@ -1,4 +1,5 @@ # mode: run +# cython: language_level=3 import cython from cython import sizeof diff --git a/tests/run/pure_py3.py b/tests/run/pure_py3.py index 908e72d56b1..2bfd8ff84f8 100644 --- a/tests/run/pure_py3.py +++ b/tests/run/pure_py3.py @@ -112,3 +112,31 @@ def test_cdef_return_object(x: object) -> object: return x else: raise RuntimeError() + + +def test_pointer_const_volatile(c_string: cython.p_const_char): + """ + >>> test_pointer_const_volatile(b'xyz') + const char * + int + volatile int + int * + const int * + b'xyz' + """ + # Using 'int' since that looks the same in Python and Cython. + a: cython.int = 5 + a_v: cython.volatile[cython.int] = 7 + p: cython.pointer[cython.int] = cython.NULL + p_c: cython.pointer[cython.const[cython.int]] = cython.NULL + + # additional compile test (cannot assign initial value): + a_c: cython.const[cython.pointer[cython.const[cython.int]]] + + print(cython.typeof(c_string) if cython.compiled else "const char *") + print(cython.typeof(a)) + print(cython.typeof(a_v) if cython.compiled else f"volatile {cython.typeof(a_v)}") + + print(cython.typeof(p) if cython.compiled else "int *") + print(cython.typeof(p_c) if cython.compiled else "const int *") + return c_string diff --git a/tests/run/py34_signature.pyx b/tests/run/py34_signature.pyx index 14780ff7b49..fc4ba821f2a 100644 --- a/tests/run/py34_signature.pyx +++ b/tests/run/py34_signature.pyx @@ -16,81 +16,137 @@ def signatures_match(f1, f2): return sig(f1), sig(f2) +lb = lambda a, b, c: None + def b(a, b, c): """ >>> def py_b(a, b, c): pass >>> signatures_match(b, py_b) + + >>> py_lb = lambda a, b, c: None + >>> signatures_match(lb, py_lb) """ +lc = lambda a, b, c=1: None + def c(a, b, c=1): """ >>> def py_c(a, b, c=1): pass >>> signatures_match(c, py_c) + + >>> py_lc = lambda a, b, c=1: None + >>> signatures_match(lc, py_lc) """ +ld = lambda a, b, *, c = 88: None + def d(a, b, *, c = 88): """ >>> def py_d(a, b, *, c = 88): pass >>> signatures_match(d, py_d) + + + >>> py_ld = lambda a, b, *, c = 88: None + >>> signatures_match(ld, py_ld) """ +le = lambda a, b, c = 88, **kwds: None + def e(a, b, c = 88, **kwds): """ >>> def py_e(a, b, c = 88, **kwds): pass >>> signatures_match(e, py_e) + + >>> py_le = lambda a, b, c = 88, **kwds: None + >>> signatures_match(le, py_le) """ +lf = lambda a, b, *, c, d = 42: None + def f(a, b, *, c, d = 42): """ >>> def py_f(a, b, *, c, d = 42): pass >>> signatures_match(f, py_f) + + >>> py_lf = lambda a, b, *, c, d = 42: None + >>> signatures_match(lf, py_lf) """ +lg = lambda a, b, *, c, d = 42, e = 17, f, **kwds: None + def g(a, b, *, c, d = 42, e = 17, f, **kwds): """ >>> def py_g(a, b, *, c, d = 42, e = 17, f, **kwds): pass >>> signatures_match(g, py_g) + + >>> py_lg = lambda a, b, *, c, d = 42, e = 17, f, **kwds: None + >>> signatures_match(lg, py_lg) """ +lh = lambda a, b, *args, c, d = 42, e = 17, f, **kwds: None + def h(a, b, *args, c, d = 42, e = 17, f, **kwds): """ >>> def py_h(a, b, *args, c, d = 42, e = 17, f, **kwds): pass >>> signatures_match(h, py_h) + + >>> py_lh = lambda a, b, *args, c, d = 42, e = 17, f, **kwds: None + >>> signatures_match(lh, py_lh) """ +lk = lambda a, b, c=1, *args, d = 42, e = 17, f, **kwds: None + def k(a, b, c=1, *args, d = 42, e = 17, f, **kwds): """ >>> def py_k(a, b, c=1, *args, d = 42, e = 17, f, **kwds): pass >>> signatures_match(k, py_k) + + >>> py_lk = lambda a, b, c=1, *args, d = 42, e = 17, f, **kwds: None + >>> signatures_match(lk, py_lk) """ +ll = lambda *, a, b, c = 88: None + def l(*, a, b, c = 88): """ >>> def py_l(*, a, b, c = 88): pass >>> signatures_match(l, py_l) + + >>> py_ll = lambda *, a, b, c = 88: None + >>> signatures_match(ll, py_ll) """ +lm = lambda a, *, b, c = 88: None + def m(a, *, b, c = 88): """ >>> def py_m(a, *, b, c = 88): pass >>> signatures_match(m, py_m) + + >>> py_lm = lambda a, *, b, c = 88: None + >>> signatures_match(lm, py_lm) """ a, b, c = b, c, a +ln = lambda a, *, b, c = 88: None + def n(a, *, b, c = 88): """ >>> def py_n(a, *, b, c = 88): pass >>> signatures_match(n, py_n) + + >>> py_ln = lambda a, *, b, c = 88: None + >>> signatures_match(ln, py_ln) """ diff --git a/tests/run/pyintop.pyx b/tests/run/pyintop.pyx index 4e12fd4204f..db99f6b1184 100644 --- a/tests/run/pyintop.pyx +++ b/tests/run/pyintop.pyx @@ -13,7 +13,7 @@ def bigints(x): print(str(x).replace('L', '')) -@cython.test_assert_path_exists('//IntBinopNode') +@cython.test_assert_path_exists('//BitwiseOrNode') def or_obj(obj2, obj3): """ >>> or_obj(2, 3) @@ -23,7 +23,7 @@ def or_obj(obj2, obj3): return obj1 -@cython.test_fail_if_path_exists('//IntBinopNode') +@cython.test_fail_if_path_exists('//BitwiseOrNode') def or_int(obj2): """ >>> or_int(0) @@ -236,7 +236,7 @@ def lshift_int(obj): @cython.test_assert_path_exists( '//IntBinopNode', - '//IntBinopNode//IntBinopNode', + '//BitwiseOrNode//IntBinopNode', ) def mixed_obj(obj2, obj3): """ @@ -248,8 +248,8 @@ def mixed_obj(obj2, obj3): @cython.test_assert_path_exists( - '//IntBinopNode', - '//IntBinopNode//PythonCapiCallNode', + '//BitwiseOrNode', + '//BitwiseOrNode//PythonCapiCallNode', ) @cython.test_fail_if_path_exists( '//IntBinopNode//IntBinopNode',