diff --git a/CHANGES.rst b/CHANGES.rst index f61d9cbe6198af192ae07de06f65350cb8524916..6615c43026145ffd70ce870310855f285afa3e9e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,27 +15,105 @@ Features added * Tracing is supported in ``nogil`` functions/sections and module init code. -* Adding/subtracting constant Python floats and small integers is faster. +* PEP 448 (Additional Unpacking Generalizations) was implemented. + +* When generators are used in a Cython module and the module imports the + modules "inspect" and/or "asyncio", Cython enables interoperability by + patching these modules to recognise Cython's internal generator type. + This can be disabled by C compiling the module with + "-D CYTHON_PATCH_ASYNCIO=0" or "-D CYTHON_PATCH_INSPECT=0" + +* When generators are used in a Cython module, the new ``Generator`` ABC + will be patched into the ``collections`` or ``collections.abc`` + stdlib module if it is not there yet. It allows type checks for + ``isinstance(obj, Generator)`` which includes both Python generators + and Cython generators. This can be disabled by C compiling the module + with "-D CYTHON_PATCH_ABC=0". See https://bugs.python.org/issue24018 + +* Adding/subtracting/dividing/modulus and equality comparisons with + constant Python floats and small integers are faster. + +* Binary and/or/xor/rshift operations with small constant Python integers + are faster. + +* Keyword argument dicts are no longer copied on function entry when they + are not being used or only passed through to other function calls (e.g. + in wrapper functions). + +* The ``PyTypeObject`` declaration in ``cpython.object`` was extended. + +* ``wraparound()`` and ``boundscheck()`` are available as no-ops in pure + Python mode. + +* Const iterators were added to the provided C++ STL declarations. + +* ``NULL`` is allowed as default argument when embedding signatures. + This fixes ticket 843. + +* When compiling with ``--embed``, the internal module name is changed to + ``__main__`` to allow arbitrary program names, including those that would + be invalid for modules. Note that this prevents reuse of the generated + C code as an importable module. Bugs fixed ---------- +* Calling "yield from" from Python on a Cython generator that returned a value + triggered a crash in CPython. This is now being worked around. + See https://bugs.python.org/issue23996 + * Language level 3 did not enable true division (a.k.a. float division) for integer operands. +* Relative cimports could accidentally fall back to trying an absolute cimport + on failure. + +* The result of calling a C struct constructor no longer requires an intermediate + assignment when coercing to a Python dict. + +* C++ exception declarations with mapping functions could fail to compile when + pre-declared in .pxd files. + + +0.22.1 (2015-05-??) +=================== + +Bugs fixed +---------- + +* Crash when returning values on generator termination. + +* In some cases, exceptions raised during internal isinstance() checks were + not propagated. + * Runtime reported file paths of source files (e.g for profiling and tracing) are now relative to the build root directory instead of the main source file. * Tracing exception handling code could enter the trace function with an active exception set. +* The internal generator function type was not shared across modules. + * Comparisons of (inferred) ctuples failed to compile. +* Closures inside of cdef functions returning ``void`` failed to compile. + +* Using ``const`` C++ references in intermediate parts of longer expressions + could fail to compile. + * C++ exception declarations with mapping functions could fail to compile when pre-declared in .pxd files. +* C++ compilation could fail with an ambiguity error in recent MacOS-X Xcode + versions. + * C compilation could fail in pypy3. +* Fixed a memory leak in the compiler when compiling multiple modules. + +* When compiling multiple modules, external library dependencies could leak + into later compiler runs. Fix by Jeroen Demeyer. This fixes ticket 845. + 0.22 (2015-02-11) ================= diff --git a/Cython/Build/Dependencies.py b/Cython/Build/Dependencies.py index baf009ff481e973bd96110ccc5342cacfb84f6fa..70004d2dd41923af2a4ba114afd1da432bc921dd 100644 --- a/Cython/Build/Dependencies.py +++ b/Cython/Build/Dependencies.py @@ -45,7 +45,7 @@ except ImportError: from distutils.extension import Extension from .. import Utils -from ..Utils import cached_function, cached_method, path_exists, find_root_package_dir +from ..Utils import cached_function, cached_method, path_exists, find_root_package_dir, is_package_dir from ..Compiler.Main import Context, CompilationOptions, default_options join_path = cached_function(os.path.join) @@ -216,12 +216,13 @@ class DistutilsInfo(object): self.values[key] = value elif type is transitive_list: if key in self.values: - all = self.values[key] + # Change a *copy* of the list (Trac #845) + all = self.values[key][:] for v in value: if v not in all: all.append(v) - else: - self.values[key] = value + value = all + self.values[key] = value return self def subs(self, aliases): @@ -250,9 +251,8 @@ class DistutilsInfo(object): for key, value in self.values.items(): type = distutils_settings[key] if type in [list, transitive_list]: - getattr(extension, key).extend(value) - else: - setattr(extension, key, value) + value = getattr(extension, key) + list(value) + setattr(extension, key, value) @cython.locals(start=long, q=long, single_q=long, double_q=long, hash_mark=long, end=long, k=long, counter=long, quote_len=long) @@ -383,7 +383,7 @@ def resolve_depend(depend, include_dirs): @cached_function def package(filename): dir = os.path.dirname(os.path.abspath(str(filename))) - if dir != filename and path_exists(join_path(dir, '__init__.py')): + if dir != filename and is_package_dir(dir): return package(dir) + (os.path.basename(dir),) else: return () @@ -832,7 +832,15 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo if not os.path.exists(options.cache): os.makedirs(options.cache) to_compile.sort() - if len(to_compile) <= 1: + # Drop "priority" component of "to_compile" entries and add a + # simple progress indicator. + N = len(to_compile) + progress_fmt = "[{0:%d}/{1}] " % len(str(N)) + for i in range(N): + progress = progress_fmt.format(i+1, N) + to_compile[i] = to_compile[i][1:] + (progress,) + + if N <= 1: nthreads = 0 if nthreads: # Requires multiprocessing (or Python >= 2.6) @@ -862,7 +870,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo pool.join() if not nthreads: for args in to_compile: - cythonize_one(*args[1:]) + cythonize_one(*args) if exclude_failures: failed_modules = set() @@ -927,7 +935,7 @@ else: # TODO: Share context? Issue: pyx processing leaks into pxd module @record_results -def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True, embedded_metadata=None): +def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_failure=True, embedded_metadata=None, progress=""): from ..Compiler.Main import compile, default_options from ..Compiler.Errors import CompileError, PyrexError @@ -944,7 +952,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f options.cache, "%s-%s%s" % (os.path.basename(c_file), fingerprint, gzip_ext)) if os.path.exists(fingerprint_file): if not quiet: - print("Found compiled %s in cache" % pyx_file) + print("%sFound compiled %s in cache" % (progress, pyx_file)) os.utime(fingerprint_file, None) g = gzip_open(fingerprint_file, 'rb') try: @@ -957,7 +965,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f g.close() return if not quiet: - print("Cythonizing %s" % pyx_file) + print("%sCythonizing %s" % (progress, pyx_file)) if options is None: options = CompilationOptions(default_options) options.output_file = c_file @@ -1000,7 +1008,7 @@ def cythonize_one(pyx_file, c_file, fingerprint, quiet, options=None, raise_on_f def cythonize_one_helper(m): import traceback try: - return cythonize_one(*m[1:]) + return cythonize_one(*m) except Exception: traceback.print_exc() raise diff --git a/Cython/Compiler/Annotate.py b/Cython/Compiler/Annotate.py index 28a0d4cc4fc53c53fc8fc7930460c3c1d1fdcd55..de5dc6e19ed7163fa6b07dec6eef3d486c42bf3d 100644 --- a/Cython/Compiler/Annotate.py +++ b/Cython/Compiler/Annotate.py @@ -277,9 +277,9 @@ _parse_code = re.compile( ur'(?P<trace>__Pyx_Trace[A-Za-z]+)|' ur'(?:' ur'(?P<pyx_macro_api>__Pyx_[A-Z][A-Z_]+)|' - ur'(?P<pyx_c_api>__Pyx_[A-Z][a-z_][A-Za-z_]+)|' + ur'(?P<pyx_c_api>__Pyx_[A-Z][a-z_][A-Za-z_]*)|' ur'(?P<py_macro_api>Py[A-Z][a-z]+_[A-Z][A-Z_]+)|' - ur'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)' + ur'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]*)' ur')(?=\()|' # look-ahead to exclude subsequent '(' from replacement ur'(?P<error_goto>(?:(?<=;) *if .* +)?\{__pyx_filename = .*goto __pyx_L\w+;\})' ).sub diff --git a/Cython/Compiler/AutoDocTransforms.py b/Cython/Compiler/AutoDocTransforms.py index 775f6358f964ac22e764c25057eba883348338b0..88b0cd88a821de2c350cbeee886aaa18780615cf 100644 --- a/Cython/Compiler/AutoDocTransforms.py +++ b/Cython/Compiler/AutoDocTransforms.py @@ -51,6 +51,8 @@ class EmbedSignature(CythonTransform): default_val = arg.default if not default_val: return None + if isinstance(default_val, ExprNodes.NullNode): + return 'NULL' try: denv = self.denv # XXX ctval = default_val.compile_time_value(self.denv) diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 18de98fea8be79833d751ecc5350eafd1a037bbd..b518abbd7db4fd8537f3e0115702a4cdde9bf363 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -165,7 +165,26 @@ builtin_function_table = [ utility_code = iter_next_utility_code), # not available in Py2 => implemented here #('oct', "", "", ""), #('open', "ss", "O", "PyFile_FromString"), # not in Py3 - #('ord', "", "", ""), +] + [ + BuiltinFunction('ord', None, None, "__Pyx_long_cast", + func_type=PyrexTypes.CFuncType( + PyrexTypes.c_long_type, [PyrexTypes.CFuncTypeArg("c", c_type, None)], + is_strict_signature=True)) + for c_type in [PyrexTypes.c_py_ucs4_type, PyrexTypes.c_py_unicode_type] +] + [ + BuiltinFunction('ord', None, None, "__Pyx_uchar_cast", + func_type=PyrexTypes.CFuncType( + PyrexTypes.c_uchar_type, [PyrexTypes.CFuncTypeArg("c", c_type, None)], + is_strict_signature=True)) + for c_type in [PyrexTypes.c_char_type, PyrexTypes.c_schar_type, PyrexTypes.c_uchar_type] +] + [ + BuiltinFunction('ord', None, None, "__Pyx_PyObject_Ord", + utility_code=UtilityCode.load_cached("object_ord", "Builtins.c"), + func_type=PyrexTypes.CFuncType( + PyrexTypes.c_long_type, [ + PyrexTypes.CFuncTypeArg("c", PyrexTypes.py_object_type, None) + ], + exception_value="(long)(Py_UCS4)-1")), BuiltinFunction('pow', "OOO", "O", "PyNumber_Power"), BuiltinFunction('pow', "OO", "O", "__Pyx_PyNumber_Power2", utility_code = UtilityCode.load("pow2", "Builtins.c")), @@ -300,6 +319,8 @@ builtin_types_table = [ BuiltinMethod("clear", "T", "r", "PySet_Clear"), # discard() and remove() have a special treatment for unhashable values # BuiltinMethod("discard", "TO", "r", "PySet_Discard"), + BuiltinMethod("update", "TO", "r", "__Pyx_PySet_Update", + utility_code=UtilityCode.load_cached("PySet_Update", "Builtins.c")), BuiltinMethod("add", "TO", "r", "PySet_Add"), BuiltinMethod("pop", "T", "O", "PySet_Pop")]), ("frozenset", "PyFrozenSet_Type", []), diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index f497bc56dc5d6f95e33dec43f7f0fcf3f6874ed3..0f4fd0ecaffed23aa4a6a56a9e2d94776272092d 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -231,12 +231,11 @@ class UtilityCodeBase(object): loader = __loader__ archive = loader.archive with closing(zipfile.ZipFile(archive)) as fileobj: - listing = [ os.path.basename(name) - for name in fileobj.namelist() - if os.path.join(archive, name).startswith(utility_dir)] - files = [ os.path.join(utility_dir, filename) - for filename in listing - if filename.startswith(prefix) ] + listing = [os.path.basename(name) + for name in fileobj.namelist() + if os.path.join(archive, name).startswith(utility_dir)] + files = [filename for filename in listing + if filename.startswith(prefix)] if not files: raise ValueError("No match found for utility code " + util_code_name) if len(files) > 1: @@ -397,6 +396,9 @@ class UtilityCode(UtilityCodeBase): def inject_string_constants(self, impl, output): """Replace 'PYIDENT("xyz")' by a constant Python identifier cname. """ + if 'PYIDENT(' not in impl: + return False, impl + replacements = {} def externalise(matchobj): name = matchobj.group(1) @@ -407,9 +409,55 @@ class UtilityCode(UtilityCodeBase): StringEncoding.EncodedString(name)).cname return cname - impl = re.sub('PYIDENT\("([^"]+)"\)', externalise, impl) + impl = re.sub(r'PYIDENT\("([^"]+)"\)', externalise, impl) + assert 'PYIDENT(' not in impl return bool(replacements), impl + def inject_unbound_methods(self, impl, output): + """Replace 'UNBOUND_METHOD(type, "name")' by a constant Python identifier cname. + """ + if 'CALL_UNBOUND_METHOD(' not in impl: + return False, impl + + utility_code = set() + def externalise(matchobj): + type_cname, method_name, args = matchobj.groups() + args = [arg.strip() for arg in args[1:].split(',')] + if len(args) == 1: + call = '__Pyx_CallUnboundCMethod0' + utility_code.add("CallUnboundCMethod0") + elif len(args) == 2: + call = '__Pyx_CallUnboundCMethod1' + utility_code.add("CallUnboundCMethod1") + else: + assert False, "CALL_UNBOUND_METHOD() requires 1 or 2 call arguments" + + cname = output.get_cached_unbound_method(type_cname, method_name, len(args)) + return '%s(&%s, %s)' % (call, cname, ', '.join(args)) + + impl = re.sub(r'CALL_UNBOUND_METHOD\(([a-zA-Z_]+),\s*"([^"]+)"((?:,\s*[^),]+)+)\)', externalise, impl) + assert 'CALL_UNBOUND_METHOD(' not in impl + + for helper in sorted(utility_code): + output.use_utility_code(UtilityCode.load_cached(helper, "ObjectHandling.c")) + return bool(utility_code), impl + + def wrap_c_strings(self, impl): + """Replace CSTRING('''xyz''') by a C compatible string + """ + if 'CSTRING(' not in impl: + return impl + + def split_string(matchobj): + content = matchobj.group(1).replace('"', '\042') + return ''.join( + '"%s\\n"\n' % line if not line.endswith('\\') or line.endswith('\\\\') else '"%s"\n' % line[:-1] + for line in content.splitlines()) + + impl = re.sub(r'CSTRING\(\s*"""([^"]+|"[^"])"""\s*\)', split_string, impl) + assert 'CSTRING(' not in impl + return impl + def put_code(self, output): if self.requires: for dependency in self.requires: @@ -419,9 +467,10 @@ class UtilityCode(UtilityCodeBase): self.format_code(self.proto), '%s_proto' % self.name) if self.impl: - impl = self.format_code(self.impl) - is_specialised, impl = self.inject_string_constants(impl, output) - if not is_specialised: + impl = self.format_code(self.wrap_c_strings(self.impl)) + is_specialised1, impl = self.inject_string_constants(impl, output) + is_specialised2, impl = self.inject_unbound_methods(impl, output) + if not (is_specialised1 or is_specialised2): # no module specific adaptations => can be reused output['utility_code_def'].put_or_include( impl, '%s_impl' % self.name) @@ -642,7 +691,7 @@ class FunctionState(object): A C string referring to the variable is returned. """ - if type.is_const: + if type.is_const and not type.is_reference: type = type.const_base_type if not type.is_pyobject and not type.is_memoryviewslice: # Make manage_ref canonical, so that manage_ref will always mean @@ -913,6 +962,7 @@ class GlobalState(object): 'typeinfo', 'before_global_var', 'global_var', + 'string_decls', 'decls', 'all_the_rest', 'pystring_table', @@ -946,6 +996,7 @@ class GlobalState(object): self.pyunicode_ptr_const_index = {} self.num_const_index = {} self.py_constants = [] + self.cached_cmethods = {} assert writer.globalstate is None writer.globalstate = self @@ -1000,7 +1051,8 @@ class GlobalState(object): # utility_code_def # code = self.parts['utility_code_def'] - code.put(UtilityCode.load_as_string("TypeConversions", "TypeConversion.c")[1]) + util = TempitaUtilityCode.load_cached("TypeConversions", "TypeConversion.c") + code.put(util.format_code(util.impl)) code.putln("") def __getitem__(self, key): @@ -1167,6 +1219,15 @@ class GlobalState(object): prefix = Naming.const_prefix return "%s%s" % (prefix, name_suffix) + def get_cached_unbound_method(self, type_cname, method_name, args_count): + key = (type_cname, method_name, args_count) + try: + cname = self.cached_cmethods[key] + except KeyError: + cname = self.cached_cmethods[key] = self.new_const_cname( + 'umethod', '%s_%s' % (type_cname, method_name)) + return cname + def add_cached_builtin_decl(self, entry): if entry.is_builtin and entry.is_const: if self.should_declare(entry.cname, entry): @@ -1198,6 +1259,7 @@ class GlobalState(object): w.error_goto(pos))) def generate_const_declarations(self): + self.generate_cached_methods_decls() self.generate_string_constants() self.generate_num_constants() self.generate_object_constant_decls() @@ -1211,13 +1273,34 @@ class GlobalState(object): decls_writer.putln( "static %s;" % c.type.declaration_code(cname)) + def generate_cached_methods_decls(self): + if not self.cached_cmethods: + return + + decl = self.parts['decls'] + init = self.parts['init_globals'] + cnames = [] + for (type_cname, method_name, _), cname in sorted(self.cached_cmethods.items()): + cnames.append(cname) + method_name_cname = self.get_interned_identifier(StringEncoding.EncodedString(method_name)).cname + decl.putln('static __Pyx_CachedCFunction %s = {0, &%s, 0, 0, 0};' % ( + cname, method_name_cname)) + # split type reference storage as it might not be static + init.putln('%s.type = (PyObject*)&%s;' % ( + cname, type_cname)) + + if Options.generate_cleanup_code: + cleanup = self.parts['cleanup_globals'] + for cname in cnames: + cleanup.putln("Py_CLEAR(%s.method);" % cname) + def generate_string_constants(self): c_consts = [ (len(c.cname), c.cname, c) for c in self.string_const_index.values() ] c_consts.sort() py_strings = [] - decls_writer = self.parts['decls'] + decls_writer = self.parts['string_decls'] for _, cname, c in c_consts: conditional = False if c.py_versions and (2 not in c.py_versions or 3 not in c.py_versions): @@ -1798,6 +1881,10 @@ class CCodeWriter(object): if entry.type.is_pyobject: self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry)) + def put_var_xincref(self, entry): + if entry.type.is_pyobject: + self.putln("__Pyx_XINCREF(%s);" % self.entry_as_pyobject(entry)) + def put_decref_clear(self, cname, type, nanny=True, clear_before_decref=False): self._put_decref(cname, type, nanny, null_check=False, clear=True, clear_before_decref=clear_before_decref) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index cd8d5e3a161f4788e57b68e063845dcd2d7ec688..dc4fb5ca704d69dac021e1efe5c2a932ea740a5e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -25,7 +25,7 @@ from .Code import UtilityCode, TempitaUtilityCode from . import StringEncoding from . import Naming from . import Nodes -from .Nodes import Node +from .Nodes import Node, utility_code_for_imports from . import PyrexTypes from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \ unspecified_type @@ -70,24 +70,28 @@ constant_value_not_set = object() # error messages when coercing from key[0] to key[1] coercion_error_dict = { # string related errors - (Builtin.unicode_type, Builtin.bytes_type) : "Cannot convert Unicode string to 'bytes' implicitly, encoding required.", - (Builtin.unicode_type, Builtin.str_type) : "Cannot convert Unicode string to 'str' implicitly. This is not portable and requires explicit encoding.", - (Builtin.unicode_type, PyrexTypes.c_char_ptr_type) : "Unicode objects only support coercion to Py_UNICODE*.", - (Builtin.unicode_type, PyrexTypes.c_uchar_ptr_type) : "Unicode objects only support coercion to Py_UNICODE*.", - (Builtin.bytes_type, Builtin.unicode_type) : "Cannot convert 'bytes' object to unicode implicitly, decoding required", - (Builtin.bytes_type, Builtin.str_type) : "Cannot convert 'bytes' object to str implicitly. This is not portable to Py3.", - (Builtin.bytes_type, Builtin.basestring_type) : "Cannot convert 'bytes' object to basestring implicitly. This is not portable to Py3.", - (Builtin.bytes_type, PyrexTypes.c_py_unicode_ptr_type) : "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'.", - (Builtin.basestring_type, Builtin.bytes_type) : "Cannot convert 'basestring' object to bytes implicitly. This is not portable.", - (Builtin.str_type, Builtin.unicode_type) : "str objects do not support coercion to unicode, use a unicode string literal instead (u'')", - (Builtin.str_type, Builtin.bytes_type) : "Cannot convert 'str' to 'bytes' implicitly. This is not portable.", - (Builtin.str_type, PyrexTypes.c_char_ptr_type) : "'str' objects do not support coercion to C types (use 'bytes'?).", - (Builtin.str_type, PyrexTypes.c_uchar_ptr_type) : "'str' objects do not support coercion to C types (use 'bytes'?).", - (Builtin.str_type, PyrexTypes.c_py_unicode_ptr_type) : "'str' objects do not support coercion to C types (use 'unicode'?).", - (PyrexTypes.c_char_ptr_type, Builtin.unicode_type) : "Cannot convert 'char*' to unicode implicitly, decoding required", - (PyrexTypes.c_uchar_ptr_type, Builtin.unicode_type) : "Cannot convert 'char*' to unicode implicitly, decoding required", + (unicode_type, str_type): ("Cannot convert Unicode string to 'str' implicitly." + " This is not portable and requires explicit encoding."), + (unicode_type, bytes_type): "Cannot convert Unicode string to 'bytes' implicitly, encoding required.", + (unicode_type, PyrexTypes.c_char_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.", + (unicode_type, PyrexTypes.c_uchar_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.", + (bytes_type, unicode_type): "Cannot convert 'bytes' object to unicode implicitly, decoding required", + (bytes_type, str_type): "Cannot convert 'bytes' object to str implicitly. This is not portable to Py3.", + (bytes_type, basestring_type): ("Cannot convert 'bytes' object to basestring implicitly." + " This is not portable to Py3."), + (bytes_type, PyrexTypes.c_py_unicode_ptr_type): "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'.", + (basestring_type, bytes_type): "Cannot convert 'basestring' object to bytes implicitly. This is not portable.", + (str_type, unicode_type): ("str objects do not support coercion to unicode," + " use a unicode string literal instead (u'')"), + (str_type, bytes_type): "Cannot convert 'str' to 'bytes' implicitly. This is not portable.", + (str_type, PyrexTypes.c_char_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).", + (str_type, PyrexTypes.c_uchar_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).", + (str_type, PyrexTypes.c_py_unicode_ptr_type): "'str' objects do not support coercion to C types (use 'unicode'?).", + (PyrexTypes.c_char_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required", + (PyrexTypes.c_uchar_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required", } + def find_coercion_error(type_tuple, default, env): err = coercion_error_dict.get(type_tuple) if err is None: @@ -293,6 +297,7 @@ class ExprNode(Node): is_sequence_constructor = False is_dict_literal = False + is_set_literal = False is_string_literal = False is_attribute = False is_subscript = False @@ -2251,7 +2256,6 @@ class ImportNode(ExprNode): name_list = self.name_list.analyse_types(env) self.name_list = name_list.coerce_to_pyobject(env) self.is_temp = 1 - env.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c")) return self gil_message = "Python import" @@ -2261,13 +2265,24 @@ class ImportNode(ExprNode): name_list_code = self.name_list.py_result() else: name_list_code = "0" - code.putln( - "%s = __Pyx_Import(%s, %s, %d); %s" % ( - self.result(), - self.module_name.py_result(), - name_list_code, - self.level, - code.error_goto_if_null(self.result(), self.pos))) + + code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c")) + import_code = "__Pyx_Import(%s, %s, %d)" % ( + self.module_name.py_result(), + name_list_code, + self.level) + + if (self.level <= 0 and + self.module_name.is_string_literal and + self.module_name.value in utility_code_for_imports): + helper_func, code_name, code_file = utility_code_for_imports[self.module_name.value] + code.globalstate.use_utility_code(UtilityCode.load_cached(code_name, code_file)) + import_code = '%s(%s)' % (helper_func, import_code) + + code.putln("%s = %s; %s" % ( + self.result(), + import_code, + code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) @@ -2470,6 +2485,7 @@ class IteratorNode(ExprNode): self.counter_cname, inc_dec, code.error_goto_if_null(result_name, self.pos))) + code.put_gotref(result_name) code.putln("#endif") def generate_iter_next_result_code(self, result_name, code): @@ -5012,17 +5028,17 @@ class PyMethodCallNode(SimpleCallNode): if len(args) > 1: code.putln("if (%s) {" % self_arg) - code.putln("PyTuple_SET_ITEM(%s, 0, %s); __Pyx_GIVEREF(%s); %s = NULL;" % ( - args_tuple, self_arg, self_arg, self_arg)) # stealing owned ref in this case + code.putln("__Pyx_GIVEREF(%s); PyTuple_SET_ITEM(%s, 0, %s); %s = NULL;" % ( + self_arg, args_tuple, self_arg, self_arg)) # stealing owned ref in this case code.funcstate.release_temp(self_arg) if len(args) > 1: code.putln("}") for i, arg in enumerate(args): arg.make_owned_reference(code) + code.put_giveref(arg.py_result()) code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % ( args_tuple, i, arg_offset, arg.py_result())) - code.put_giveref(arg.py_result()) if len(args) > 1: code.funcstate.release_temp(arg_offset_cname) @@ -5214,8 +5230,8 @@ class GeneralCallNode(CallNode): self.compile_time_value_error(e) def explicit_args_kwds(self): - if (self.keyword_args and not isinstance(self.keyword_args, DictNode) or - not isinstance(self.positional_args, TupleNode)): + if (self.keyword_args and not self.keyword_args.is_dict_literal or + not self.positional_args.is_sequence_constructor): raise CompileError(self.pos, 'Compile-time keyword arguments must be explicit.') return self.positional_args.args, self.keyword_args @@ -5269,7 +5285,7 @@ class GeneralCallNode(CallNode): if not isinstance(self.positional_args, TupleNode): # has starred argument return self - if not isinstance(self.keyword_args, DictNode): + if not self.keyword_args.is_dict_literal: # keywords come from arbitrary expression => nothing to do here return self function = self.function @@ -5435,8 +5451,9 @@ class AsTupleNode(ExprNode): self.compile_time_value_error(e) def analyse_types(self, env): - self.arg = self.arg.analyse_types(env) - self.arg = self.arg.coerce_to_pyobject(env) + self.arg = self.arg.analyse_types(env).coerce_to_pyobject(env) + if self.arg.type is tuple_type: + return self.arg.as_none_safe_node("'NoneType' object is not iterable") self.type = tuple_type self.is_temp = 1 return self @@ -5456,6 +5473,168 @@ class AsTupleNode(ExprNode): code.put_gotref(self.py_result()) +class MergedDictNode(ExprNode): + # Helper class for keyword arguments and other merged dicts. + # + # keyword_args [DictNode or other ExprNode] + + subexprs = ['keyword_args'] + is_temp = 1 + type = dict_type + reject_duplicates = True + + def calculate_constant_result(self): + result = {} + reject_duplicates = self.reject_duplicates + for item in self.keyword_args: + if item.is_dict_literal: + # process items in order + items = ((key.constant_result, value.constant_result) + for key, value in item.key_value_pairs) + else: + items = item.constant_result.iteritems() + + for key, value in items: + if reject_duplicates and key in result: + raise ValueError("duplicate keyword argument found: %s" % key) + result[key] = value + + self.constant_result = result + + def compile_time_value(self, denv): + result = {} + reject_duplicates = self.reject_duplicates + for item in self.keyword_args: + if item.is_dict_literal: + # process items in order + items = [(key.compile_time_value(denv), value.compile_time_value(denv)) + for key, value in item.key_value_pairs] + else: + items = item.compile_time_value(denv).iteritems() + + try: + for key, value in items: + if reject_duplicates and key in result: + raise ValueError("duplicate keyword argument found: %s" % key) + result[key] = value + except Exception, e: + self.compile_time_value_error(e) + return result + + def type_dependencies(self, env): + return () + + def infer_type(self, env): + return dict_type + + def analyse_types(self, env): + args = [ + arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node( + # FIXME: CPython's error message starts with the runtime function name + 'argument after ** must be a mapping, not NoneType') + for arg in self.keyword_args + ] + + if len(args) == 1 and args[0].type is dict_type: + # strip this intermediate node and use the bare dict + arg = args[0] + if arg.is_name and arg.entry.is_arg and len(arg.entry.cf_assignments) == 1: + # passing **kwargs through to function call => allow NULL + arg.allow_null = True + return arg + + self.keyword_args = args + return self + + def may_be_none(self): + return False + + gil_message = "Constructing Python dict" + + def generate_evaluation_code(self, code): + code.mark_pos(self.pos) + self.allocate_temp_result(code) + + args = iter(self.keyword_args) + item = next(args) + item.generate_evaluation_code(code) + if item.type is not dict_type: + # CPython supports calling functions with non-dicts, so do we + code.putln('if (likely(PyDict_CheckExact(%s))) {' % + item.py_result()) + + if item.is_dict_literal: + item.make_owned_reference(code) + code.putln("%s = %s;" % (self.result(), item.py_result())) + item.generate_post_assignment_code(code) + else: + code.putln("%s = PyDict_Copy(%s); %s" % ( + self.result(), + item.py_result(), + code.error_goto_if_null(self.result(), item.pos))) + code.put_gotref(self.result()) + item.generate_disposal_code(code) + + if item.type is not dict_type: + code.putln('} else {') + code.putln("%s = PyObject_CallFunctionObjArgs((PyObject*)&PyDict_Type, %s, NULL); %s" % ( + self.result(), + item.py_result(), + code.error_goto_if_null(self.result(), self.pos))) + code.put_gotref(self.py_result()) + item.generate_disposal_code(code) + code.putln('}') + item.free_temps(code) + + helpers = set() + for item in args: + if item.is_dict_literal: + # inline update instead of creating an intermediate dict + for arg in item.key_value_pairs: + arg.generate_evaluation_code(code) + if self.reject_duplicates: + code.putln("if (unlikely(PyDict_Contains(%s, %s))) {" % ( + self.result(), + arg.key.py_result())) + helpers.add("RaiseDoubleKeywords") + # FIXME: find out function name at runtime! + code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % ( + arg.key.py_result(), + code.error_goto(self.pos))) + code.putln("}") + code.put_error_if_neg(arg.key.pos, "PyDict_SetItem(%s, %s, %s)" % ( + self.result(), + arg.key.py_result(), + arg.value.py_result())) + arg.generate_disposal_code(code) + arg.free_temps(code) + else: + item.generate_evaluation_code(code) + if self.reject_duplicates: + # merge mapping into kwdict one by one as we need to check for duplicates + helpers.add("MergeKeywords") + code.put_error_if_neg(item.pos, "__Pyx_MergeKeywords(%s, %s)" % ( + self.result(), item.py_result())) + else: + # simple case, just add all entries + helpers.add("RaiseMappingExpected") + code.putln("if (unlikely(PyDict_Update(%s, %s) < 0)) {" % ( + self.result(), item.py_result())) + code.putln("if (PyErr_ExceptionMatches(PyExc_AttributeError)) " + "__Pyx_RaiseMappingExpectedError(%s);" % item.py_result()) + code.putln(code.error_goto(item.pos)) + code.putln("}") + item.generate_disposal_code(code) + item.free_temps(code) + + for helper in sorted(helpers): + code.globalstate.use_utility_code(UtilityCode.load_cached(helper, "FunctionArguments.c")) + + def annotate(self, code): + for item in self.keyword_args: + item.annotate(code) + + class AttributeNode(ExprNode): # obj.attribute # @@ -5992,14 +6171,14 @@ class AttributeNode(ExprNode): # #------------------------------------------------------------------- -class StarredTargetNode(ExprNode): +class StarredUnpackingNode(ExprNode): # A starred expression like "*a" # - # This is only allowed in sequence assignment targets such as + # This is only allowed in sequence assignment or construction such as # # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4] # - # and will be removed during type analysis (or generate an error + # and will be special cased during type analysis (or generate an error # if it's found at unexpected places). # # target ExprNode @@ -6008,17 +6187,22 @@ class StarredTargetNode(ExprNode): is_starred = 1 type = py_object_type is_temp = 1 + starred_expr_allowed_here = False def __init__(self, pos, target): - ExprNode.__init__(self, pos) - self.target = target + ExprNode.__init__(self, pos, target=target) def analyse_declarations(self, env): - error(self.pos, "can use starred expression only as assignment target") + if not self.starred_expr_allowed_here: + error(self.pos, "starred expression is not allowed here") self.target.analyse_declarations(env) + def infer_type(self, env): + return self.target.infer_type(env) + def analyse_types(self, env): - error(self.pos, "can use starred expression only as assignment target") + if not self.starred_expr_allowed_here: + error(self.pos, "starred expression is not allowed here") self.target = self.target.analyse_types(env) self.type = self.target.type return self @@ -6077,9 +6261,9 @@ class SequenceNode(ExprNode): arg.analyse_target_declaration(env) def analyse_types(self, env, skip_children=False): - for i in range(len(self.args)): - arg = self.args[i] - if not skip_children: arg = arg.analyse_types(env) + for i, arg in enumerate(self.args): + if not skip_children: + arg = arg.analyse_types(env) self.args[i] = arg.coerce_to_pyobject(env) if self.mult_factor: self.mult_factor = self.mult_factor.analyse_types(env) @@ -6089,6 +6273,39 @@ class SequenceNode(ExprNode): # not setting self.type here, subtypes do this return self + def _create_merge_node_if_necessary(self, env): + self._flatten_starred_args() + if not any(arg.is_starred for arg in self.args): + return self + # convert into MergedSequenceNode by building partial sequences + args = [] + values = [] + for arg in self.args: + if arg.is_starred: + if values: + args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True)) + values = [] + args.append(arg.target) + else: + values.append(arg) + if values: + args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True)) + node = MergedSequenceNode(self.pos, args, self.type) + if self.mult_factor: + node = binop_node( + self.pos, '*', node, self.mult_factor.coerce_to_pyobject(env), + inplace=True, type=self.type, is_temp=True) + return node + + def _flatten_starred_args(self): + args = [] + for arg in self.args: + if arg.is_starred and arg.target.is_sequence_constructor and not arg.target.mult_factor: + args.extend(arg.target.args) + else: + args.append(arg) + self.args[:] = args + def may_be_none(self): return False @@ -6101,11 +6318,11 @@ class SequenceNode(ExprNode): for i, arg in enumerate(self.args): arg = self.args[i] = arg.analyse_target_types(env) if arg.is_starred: - if not arg.type.assignable_from(Builtin.list_type): + if not arg.type.assignable_from(list_type): error(arg.pos, "starred target must have Python object (list) type") if arg.type is py_object_type: - arg.type = Builtin.list_type + arg.type = list_type unpacked_item = PyTempNode(self.pos, env) coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env) if unpacked_item is not coerced_unpacked_item: @@ -6136,12 +6353,12 @@ class SequenceNode(ExprNode): else: size_factor = ' * (%s)' % (c_mult,) - if self.type is Builtin.tuple_type and (self.is_literal or self.slow) and not c_mult: + if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult: # use PyTuple_Pack() to avoid generating huge amounts of one-time code code.putln('%s = PyTuple_Pack(%d, %s); %s' % ( target, len(self.args), - ', '.join([ arg.py_result() for arg in self.args ]), + ', '.join(arg.py_result() for arg in self.args), code.error_goto_if_null(target, self.pos))) code.put_gotref(target) elif self.type.is_ctuple: @@ -6150,9 +6367,9 @@ class SequenceNode(ExprNode): target, i, arg.result())) else: # build the tuple/list step by step, potentially multiplying it as we go - if self.type is Builtin.list_type: + if self.type is list_type: create_func, set_item_func = 'PyList_New', 'PyList_SET_ITEM' - elif self.type is Builtin.tuple_type: + elif self.type is tuple_type: create_func, set_item_func = 'PyTuple_New', 'PyTuple_SET_ITEM' else: raise InternalError("sequence packing for unexpected type %s" % self.type) @@ -6184,12 +6401,12 @@ class SequenceNode(ExprNode): arg = self.args[i] if c_mult or not arg.result_in_temp(): code.put_incref(arg.result(), arg.ctype()) + code.put_giveref(arg.py_result()) code.putln("%s(%s, %s, %s);" % ( set_item_func, target, (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), arg.py_result())) - code.put_giveref(arg.py_result()) if c_mult: code.putln('}') @@ -6209,7 +6426,7 @@ class SequenceNode(ExprNode): def generate_subexpr_disposal_code(self, code): if self.mult_factor and self.mult_factor.type.is_int: super(SequenceNode, self).generate_subexpr_disposal_code(code) - elif self.type is Builtin.tuple_type and (self.is_literal or self.slow): + elif self.type is tuple_type and (self.is_literal or self.slow): super(SequenceNode, self).generate_subexpr_disposal_code(code) else: # We call generate_post_assignment_code here instead @@ -6528,39 +6745,42 @@ class TupleNode(SequenceNode): if any(type.is_pyobject or type.is_unspecified or type.is_fused for type in arg_types): return tuple_type else: - type = PyrexTypes.c_tuple_type(arg_types) - env.declare_tuple_type(self.pos, type) - return type + return env.declare_tuple_type(self.pos, arg_types).type def analyse_types(self, env, skip_children=False): if len(self.args) == 0: self.is_temp = False self.is_literal = True return self - else: - if not skip_children: - self.args = [arg.analyse_types(env) for arg in self.args] - if not self.mult_factor and not any(arg.type.is_pyobject or arg.type.is_fused for arg in self.args): - self.type = PyrexTypes.c_tuple_type(arg.type for arg in self.args) - env.declare_tuple_type(self.pos, self.type) - self.is_temp = 1 - return self - else: - node = SequenceNode.analyse_types(self, env, skip_children=True) - for child in node.args: - if not child.is_literal: - break - else: - if not node.mult_factor or node.mult_factor.is_literal and \ - isinstance(node.mult_factor.constant_result, (int, long)): - node.is_temp = False - node.is_literal = True - else: - if not node.mult_factor.type.is_pyobject: - node.mult_factor = node.mult_factor.coerce_to_pyobject(env) - node.is_temp = True - node.is_partly_literal = True - return node + + if not skip_children: + for i, arg in enumerate(self.args): + if arg.is_starred: + arg.starred_expr_allowed_here = True + self.args[i] = arg.analyse_types(env) + if (not self.mult_factor and + not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_fused) for arg in self.args)): + self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type + self.is_temp = 1 + return self + + node = SequenceNode.analyse_types(self, env, skip_children=True) + node = node._create_merge_node_if_necessary(env) + if not node.is_sequence_constructor: + return node + + if not all(child.is_literal for child in node.args): + return node + if not node.mult_factor or ( + node.mult_factor.is_literal and isinstance(node.mult_factor.constant_result, (int, long))): + node.is_temp = False + node.is_literal = True + else: + if not node.mult_factor.type.is_pyobject: + node.mult_factor = node.mult_factor.coerce_to_pyobject(env) + node.is_temp = True + node.is_partly_literal = True + return node def coerce_to(self, dst_type, env): if self.type.is_ctuple: @@ -6657,6 +6877,9 @@ class ListNode(SequenceNode): return list_type def analyse_expressions(self, env): + for arg in self.args: + if arg.is_starred: + arg.starred_expr_allowed_here = True node = SequenceNode.analyse_expressions(self, env) return node.coerce_to_pyobject(env) @@ -6668,6 +6891,7 @@ class ListNode(SequenceNode): release_errors(ignore=True) if env.is_module_scope: self.in_module_scope = True + node = node._create_merge_node_if_necessary(env) return node def coerce_to(self, dst_type, env): @@ -7062,13 +7286,182 @@ class InlinedGeneratorExpressionNode(ScopedExprNode): self.loop.generate_execution_code(code) -class SetNode(ExprNode): - # Set constructor. +class MergedSequenceNode(ExprNode): + """ + Merge a sequence of iterables into a set/list/tuple. - type = set_type + The target collection is determined by self.type, which must be set externally. + args [ExprNode] + """ subexprs = ['args'] + is_temp = True + gil_message = "Constructing Python collection" + + def __init__(self, pos, args, type): + if type in (list_type, tuple_type) and args and args[0].is_sequence_constructor: + # construct a list directly from the first argument that we can then extend + if args[0].type is not list_type: + args[0] = ListNode(args[0].pos, args=args[0].args, is_temp=True) + ExprNode.__init__(self, pos, args=args, type=type) + + def calculate_constant_result(self): + result = [] + for item in self.args: + if item.is_sequence_constructor and item.mult_factor: + if item.mult_factor.constant_result <= 0: + continue + # otherwise, adding each item once should be enough + if item.is_set_literal or item.is_sequence_constructor: + # process items in order + items = (arg.constant_result for arg in item.args) + else: + items = item.constant_result + result.extend(items) + if self.type is set_type: + result = set(result) + elif self.type is tuple_type: + result = tuple(result) + else: + assert self.type is list_type + self.constant_result = result + + def compile_time_value(self, denv): + result = [] + for item in self.args: + if item.is_sequence_constructor and item.mult_factor: + if item.mult_factor.compile_time_value(denv) <= 0: + continue + if item.is_set_literal or item.is_sequence_constructor: + # process items in order + items = (arg.compile_time_value(denv) for arg in item.args) + else: + items = item.compile_time_value(denv) + result.extend(items) + if self.type is set_type: + try: + result = set(result) + except Exception as e: + self.compile_time_value_error(e) + elif self.type is tuple_type: + result = tuple(result) + else: + assert self.type is list_type + return result + + def type_dependencies(self, env): + return () + + def infer_type(self, env): + return self.type + + def analyse_types(self, env): + args = [ + arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node( + # FIXME: CPython's error message starts with the runtime function name + 'argument after * must be an iterable, not NoneType') + for arg in self.args + ] + + if len(args) == 1 and args[0].type is self.type: + # strip this intermediate node and use the bare collection + return args[0] + + assert self.type in (set_type, list_type, tuple_type) + + self.args = args + return self + + def may_be_none(self): + return False + + def generate_evaluation_code(self, code): + code.mark_pos(self.pos) + self.allocate_temp_result(code) + + is_set = self.type is set_type + + args = iter(self.args) + item = next(args) + item.generate_evaluation_code(code) + if (is_set and item.is_set_literal or + not is_set and item.is_sequence_constructor and item.type is list_type): + code.putln("%s = %s;" % (self.result(), item.py_result())) + item.generate_post_assignment_code(code) + else: + code.putln("%s = %s(%s); %s" % ( + self.result(), + 'PySet_New' if is_set else 'PySequence_List', + item.py_result(), + code.error_goto_if_null(self.result(), self.pos))) + code.put_gotref(self.py_result()) + item.generate_disposal_code(code) + item.free_temps(code) + + helpers = set() + if is_set: + add_func = "PySet_Add" + extend_func = "__Pyx_PySet_Update" + else: + add_func = "__Pyx_ListComp_Append" + extend_func = "__Pyx_PyList_Extend" + + for item in args: + if (is_set and (item.is_set_literal or item.is_sequence_constructor) or + (item.is_sequence_constructor and not item.mult_factor)): + if not is_set and item.args: + helpers.add(("ListCompAppend", "Optimize.c")) + for arg in item.args: + arg.generate_evaluation_code(code) + code.put_error_if_neg(arg.pos, "%s(%s, %s)" % ( + add_func, + self.result(), + arg.py_result())) + arg.generate_disposal_code(code) + arg.free_temps(code) + continue + + if is_set: + helpers.add(("PySet_Update", "Builtins.c")) + else: + helpers.add(("ListExtend", "Optimize.c")) + + item.generate_evaluation_code(code) + code.put_error_if_neg(item.pos, "%s(%s, %s)" % ( + extend_func, + self.result(), + item.py_result())) + item.generate_disposal_code(code) + item.free_temps(code) + + if self.type is tuple_type: + code.putln("{") + code.putln("PyObject *%s = PyList_AsTuple(%s);" % ( + Naming.quick_temp_cname, + self.result())) + code.put_decref(self.result(), py_object_type) + code.putln("%s = %s; %s" % ( + self.result(), + Naming.quick_temp_cname, + code.error_goto_if_null(self.result(), self.pos))) + code.put_gotref(self.result()) + code.putln("}") + + for helper in sorted(helpers): + code.globalstate.use_utility_code(UtilityCode.load_cached(*helper)) + def annotate(self, code): + for item in self.args: + item.annotate(code) + + +class SetNode(ExprNode): + """ + Set constructor. + """ + subexprs = ['args'] + type = set_type + is_set_literal = True gil_message = "Constructing Python set" def analyse_types(self, env): @@ -7123,6 +7516,7 @@ class DictNode(ExprNode): exclude_null_values = False type = dict_type is_dict_literal = True + reject_duplicates = False obj_conversion_errors = [] @@ -7164,6 +7558,13 @@ class DictNode(ExprNode): def coerce_to(self, dst_type, env): if dst_type.is_pyobject: self.release_errors() + if self.type.is_struct_or_union: + if not dict_type.subtype_of(dst_type): + error(self.pos, "Cannot interpret struct as non-dict type '%s'" % dst_type) + return DictNode(self.pos, key_value_pairs=[ + DictItemNode(item.pos, key=item.key.coerce_to_pyobject(env), + value=item.value.coerce_to_pyobject(env)) + for item in self.key_value_pairs]) if not self.type.subtype_of(dst_type): error(self.pos, "Cannot interpret dict as type '%s'" % dst_type) elif dst_type.is_struct_or_union: @@ -7205,23 +7606,60 @@ class DictNode(ExprNode): # pairs are evaluated and used one at a time. code.mark_pos(self.pos) self.allocate_temp_result(code) - if self.type.is_pyobject: + + is_dict = self.type.is_pyobject + if is_dict: self.release_errors() code.putln( "%s = PyDict_New(); %s" % ( self.result(), code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) + + keys_seen = set() + key_type = None + needs_error_helper = False + for item in self.key_value_pairs: item.generate_evaluation_code(code) - if self.type.is_pyobject: + if is_dict: if self.exclude_null_values: code.putln('if (%s) {' % item.value.py_result()) - code.put_error_if_neg(self.pos, - "PyDict_SetItem(%s, %s, %s)" % ( - self.result(), - item.key.py_result(), - item.value.py_result())) + key = item.key + if self.reject_duplicates: + if keys_seen is not None: + # avoid runtime 'in' checks for literals that we can do at compile time + if not key.is_string_literal: + keys_seen = None + elif key.value in keys_seen: + # FIXME: this could be a compile time error, at least in Cython code + keys_seen = None + elif key_type is not type(key.value): + if key_type is None: + key_type = type(key.value) + keys_seen.add(key.value) + else: + # different types => may not be able to compare at compile time + keys_seen = None + else: + keys_seen.add(key.value) + + if keys_seen is None: + code.putln('if (unlikely(PyDict_Contains(%s, %s))) {' % ( + self.result(), key.py_result())) + # currently only used in function calls + needs_error_helper = True + code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % ( + key.py_result(), + code.error_goto(item.pos))) + code.putln("} else {") + + code.put_error_if_neg(self.pos, "PyDict_SetItem(%s, %s, %s)" % ( + self.result(), + item.key.py_result(), + item.value.py_result())) + if self.reject_duplicates and keys_seen is None: + code.putln('}') if self.exclude_null_values: code.putln('}') else: @@ -7232,10 +7670,15 @@ class DictNode(ExprNode): item.generate_disposal_code(code) item.free_temps(code) + if needs_error_helper: + code.globalstate.use_utility_code( + UtilityCode.load_cached("RaiseDoubleKeywords", "FunctionArguments.c")) + def annotate(self, code): for item in self.key_value_pairs: item.annotate(code) + class DictItemNode(ExprNode): # Represents a single item in a DictNode # @@ -7435,120 +7878,6 @@ class Py3ClassNode(ExprNode): code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) -class KeywordArgsNode(ExprNode): - # Helper class for keyword arguments. - # - # starstar_arg DictNode - # keyword_args [DictItemNode] - - subexprs = ['starstar_arg', 'keyword_args'] - is_temp = 1 - type = dict_type - - def calculate_constant_result(self): - result = dict(self.starstar_arg.constant_result) - for item in self.keyword_args: - key, value = item.constant_result - if key in result: - raise ValueError("duplicate keyword argument found: %s" % key) - result[key] = value - self.constant_result = result - - def compile_time_value(self, denv): - result = self.starstar_arg.compile_time_value(denv) - pairs = [ (item.key.compile_time_value(denv), item.value.compile_time_value(denv)) - for item in self.keyword_args ] - try: - result = dict(result) - for key, value in pairs: - if key in result: - raise ValueError("duplicate keyword argument found: %s" % key) - result[key] = value - except Exception, e: - self.compile_time_value_error(e) - return result - - def type_dependencies(self, env): - return () - - def infer_type(self, env): - return dict_type - - def analyse_types(self, env): - arg = self.starstar_arg.analyse_types(env) - arg = arg.coerce_to_pyobject(env) - self.starstar_arg = arg.as_none_safe_node( - # FIXME: CPython's error message starts with the runtime function name - 'argument after ** must be a mapping, not NoneType') - self.keyword_args = [ item.analyse_types(env) - for item in self.keyword_args ] - return self - - def may_be_none(self): - return False - - gil_message = "Constructing Python dict" - - def generate_evaluation_code(self, code): - code.mark_pos(self.pos) - self.allocate_temp_result(code) - self.starstar_arg.generate_evaluation_code(code) - if self.starstar_arg.type is not Builtin.dict_type: - # CPython supports calling functions with non-dicts, so do we - code.putln('if (likely(PyDict_Check(%s))) {' % - self.starstar_arg.py_result()) - if self.keyword_args: - code.putln( - "%s = PyDict_Copy(%s); %s" % ( - self.result(), - self.starstar_arg.py_result(), - code.error_goto_if_null(self.result(), self.pos))) - code.put_gotref(self.py_result()) - else: - code.putln("%s = %s;" % ( - self.result(), - self.starstar_arg.py_result())) - code.put_incref(self.result(), py_object_type) - if self.starstar_arg.type is not Builtin.dict_type: - code.putln('} else {') - code.putln( - "%s = PyObject_CallFunctionObjArgs(" - "(PyObject*)&PyDict_Type, %s, NULL); %s" % ( - self.result(), - self.starstar_arg.py_result(), - code.error_goto_if_null(self.result(), self.pos))) - code.put_gotref(self.py_result()) - code.putln('}') - self.starstar_arg.generate_disposal_code(code) - self.starstar_arg.free_temps(code) - - if not self.keyword_args: - return - - code.globalstate.use_utility_code( - UtilityCode.load_cached("RaiseDoubleKeywords", "FunctionArguments.c")) - for item in self.keyword_args: - item.generate_evaluation_code(code) - code.putln("if (unlikely(PyDict_GetItem(%s, %s))) {" % ( - self.result(), - item.key.py_result())) - # FIXME: find out function name at runtime! - code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % ( - item.key.py_result(), - code.error_goto(self.pos))) - code.putln("}") - code.put_error_if_neg(self.pos, - "PyDict_SetItem(%s, %s, %s)" % ( - self.result(), - item.key.py_result(), - item.value.py_result())) - item.generate_disposal_code(code) - item.free_temps(code) - - def annotate(self, code): - self.starstar_arg.annotate(code) - for item in self.keyword_args: - item.annotate(code) class PyClassMetaclassNode(ExprNode): # Helper class holds Python3 metaclass object @@ -7660,7 +7989,7 @@ class ClassCellInjectorNode(ExprNode): def generate_injection_code(self, code, classobj_cname): if self.is_active: - code.putln('__Pyx_CyFunction_InitClassCell(%s, %s);' % ( + code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % ( self.result(), classobj_cname)) @@ -9821,11 +10150,13 @@ class DivNode(NumBinopNode): or not self.type.signed or self.type.is_float) if not self.cdivision: - code.globalstate.use_utility_code(div_int_utility_code.specialize(self.type)) + code.globalstate.use_utility_code( + UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type)) NumBinopNode.generate_evaluation_code(self, code) self.generate_div_warning_code(code) def generate_div_warning_code(self, code): + in_nogil = self.in_nogil_context if not self.type.is_pyobject: if self.zerodivision_check: if not self.infix: @@ -9833,13 +10164,15 @@ class DivNode(NumBinopNode): else: zero_test = "%s == 0" % self.operand2.result() code.putln("if (unlikely(%s)) {" % zero_test) - code.put_ensure_gil() + if in_nogil: + code.put_ensure_gil() code.putln('PyErr_SetString(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message()) - code.put_release_ensured_gil() + if in_nogil: + code.put_release_ensured_gil() code.putln(code.error_goto(self.pos)) code.putln("}") if self.type.is_int and self.type.signed and self.operator != '%': - code.globalstate.use_utility_code(division_overflow_test_code) + code.globalstate.use_utility_code(UtilityCode.load_cached("UnaryNegOverflows", "Overflow.c")) if self.operand2.type.signed == 2: # explicitly signed, no runtime check needed minus1_check = 'unlikely(%s == -1)' % self.operand2.result() @@ -9852,28 +10185,38 @@ class DivNode(NumBinopNode): self.type.empty_declaration_code(), minus1_check, self.operand1.result())) - code.put_ensure_gil() + if in_nogil: + code.put_ensure_gil() code.putln('PyErr_SetString(PyExc_OverflowError, "value too large to perform division");') - code.put_release_ensured_gil() + if in_nogil: + code.put_release_ensured_gil() code.putln(code.error_goto(self.pos)) code.putln("}") if code.globalstate.directives['cdivision_warnings'] and self.operator != '/': - code.globalstate.use_utility_code(cdivision_warning_utility_code) + code.globalstate.use_utility_code( + UtilityCode.load_cached("CDivisionWarning", "CMath.c")) code.putln("if (unlikely((%s < 0) ^ (%s < 0))) {" % ( self.operand1.result(), self.operand2.result())) - code.put_ensure_gil() - code.putln(code.set_error_info(self.pos, used=True)) - code.putln("if (__Pyx_cdivision_warning(%(FILENAME)s, " - "%(LINENO)s)) {" % { + warning_code = "__Pyx_cdivision_warning(%(FILENAME)s, %(LINENO)s)" % { 'FILENAME': Naming.filename_cname, 'LINENO': Naming.lineno_cname, - }) - code.put_release_ensured_gil() + } + + if in_nogil: + result_code = 'result' + code.putln("int %s;" % result_code) + code.put_ensure_gil() + code.putln(code.set_error_info(self.pos, used=True)) + code.putln("%s = %s;" % (result_code, warning_code)) + code.put_release_ensured_gil() + else: + result_code = warning_code + code.putln(code.set_error_info(self.pos, used=True)) + + code.put("if (unlikely(%s)) " % result_code) code.put_goto(code.error_label) code.putln("}") - code.put_release_ensured_gil() - code.putln("}") def calculate_result_code(self): if self.type.is_complex: @@ -9941,12 +10284,12 @@ class ModNode(DivNode): if not self.type.is_pyobject and not self.cdivision: if self.type.is_int: code.globalstate.use_utility_code( - mod_int_utility_code.specialize(self.type)) + UtilityCode.load_cached("ModInt", "CMath.c").specialize(self.type)) else: # float code.globalstate.use_utility_code( - mod_float_utility_code.specialize( + UtilityCode.load_cached("ModFloat", "CMath.c").specialize( self.type, math_h_modifier=self.type.math_h_modifier)) - # note: skipping over DivNode here + # NOTE: skipping over DivNode here NumBinopNode.generate_evaluation_code(self, code) self.generate_div_warning_code(code) @@ -9999,7 +10342,7 @@ class PowNode(NumBinopNode): elif self.type.is_int: self.pow_func = "__Pyx_pow_%s" % self.type.empty_declaration_code().replace(' ', '_') env.use_utility_code( - int_pow_utility_code.specialize( + UtilityCode.load_cached("IntPow", "CMath.c").specialize( func_name=self.pow_func, type=self.type.empty_declaration_code(), signed=self.type.signed and 1 or 0)) @@ -10615,18 +10958,18 @@ class CmpNode(object): if self.operand2.type is Builtin.dict_type: self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable") self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c") - self.special_bool_cmp_function = "__Pyx_PyDict_Contains" + self.special_bool_cmp_function = "__Pyx_PyDict_ContainsTF" return True elif self.operand2.type is Builtin.unicode_type: self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable") self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c") - self.special_bool_cmp_function = "__Pyx_PyUnicode_Contains" + self.special_bool_cmp_function = "__Pyx_PyUnicode_ContainsTF" return True else: if not self.operand2.type.is_pyobject: self.operand2 = self.operand2.coerce_to_pyobject(env) self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySequenceContains", "ObjectHandling.c") - self.special_bool_cmp_function = "__Pyx_PySequence_Contains" + self.special_bool_cmp_function = "__Pyx_PySequence_ContainsTF" return True return False @@ -11466,11 +11809,13 @@ class CoerceToBooleanNode(CoercionNode): type = PyrexTypes.c_bint_type _special_builtins = { - Builtin.list_type : 'PyList_GET_SIZE', - Builtin.tuple_type : 'PyTuple_GET_SIZE', - Builtin.bytes_type : 'PyBytes_GET_SIZE', - Builtin.unicode_type : 'PyUnicode_GET_SIZE', - } + Builtin.list_type: 'PyList_GET_SIZE', + Builtin.tuple_type: 'PyTuple_GET_SIZE', + Builtin.set_type: 'PySet_GET_SIZE', + Builtin.frozenset_type: 'PySet_GET_SIZE', + Builtin.bytes_type: 'PyBytes_GET_SIZE', + Builtin.unicode_type: 'PyUnicode_GET_SIZE', + } def __init__(self, arg, env): CoercionNode.__init__(self, arg) @@ -11509,6 +11854,7 @@ class CoerceToBooleanNode(CoercionNode): self.arg.py_result(), code.error_goto_if_neg(self.result(), self.pos))) + class CoerceToComplexNode(CoercionNode): def __init__(self, arg, dst_type, env): @@ -11810,103 +12156,3 @@ requires = [raise_unbound_local_error_utility_code]) raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c") raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c") tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c") - -#------------------------------------------------------------------------------------ - -int_pow_utility_code = UtilityCode( -proto=""" -static CYTHON_INLINE %(type)s %(func_name)s(%(type)s, %(type)s); /* proto */ -""", -impl=""" -static CYTHON_INLINE %(type)s %(func_name)s(%(type)s b, %(type)s e) { - %(type)s t = b; - switch (e) { - case 3: - t *= b; - case 2: - t *= b; - case 1: - return t; - case 0: - return 1; - } - #if %(signed)s - if (unlikely(e<0)) return 0; - #endif - t = 1; - while (likely(e)) { - t *= (b * (e&1)) | ((~e)&1); /* 1 or b */ - b *= b; - e >>= 1; - } - return t; -} -""") - -# ------------------------------ Division ------------------------------------ - -div_int_utility_code = UtilityCode( -proto=""" -static CYTHON_INLINE %(type)s __Pyx_div_%(type_name)s(%(type)s, %(type)s); /* proto */ -""", -impl=""" -static CYTHON_INLINE %(type)s __Pyx_div_%(type_name)s(%(type)s a, %(type)s b) { - %(type)s q = a / b; - %(type)s r = a - q*b; - q -= ((r != 0) & ((r ^ b) < 0)); - return q; -} -""") - -mod_int_utility_code = UtilityCode( -proto=""" -static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s, %(type)s); /* proto */ -""", -impl=""" -static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s a, %(type)s b) { - %(type)s r = a %% b; - r += ((r != 0) & ((r ^ b) < 0)) * b; - return r; -} -""") - -mod_float_utility_code = UtilityCode( -proto=""" -static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s, %(type)s); /* proto */ -""", -impl=""" -static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s a, %(type)s b) { - %(type)s r = fmod%(math_h_modifier)s(a, b); - r += ((r != 0) & ((r < 0) ^ (b < 0))) * b; - return r; -} -""") - -cdivision_warning_utility_code = UtilityCode( -proto=""" -static int __Pyx_cdivision_warning(const char *, int); /* proto */ -""", -impl=""" -static int __Pyx_cdivision_warning(const char *filename, int lineno) { -#if CYTHON_COMPILING_IN_PYPY - filename++; // avoid compiler warnings - lineno++; - return PyErr_Warn(PyExc_RuntimeWarning, - "division with oppositely signed operands, C and Python semantics differ"); -#else - return PyErr_WarnExplicit(PyExc_RuntimeWarning, - "division with oppositely signed operands, C and Python semantics differ", - filename, - lineno, - __Pyx_MODULE_NAME, - NULL); -#endif -} -""") - -# from intobject.c -division_overflow_test_code = UtilityCode( -proto=""" -#define UNARY_NEG_WOULD_OVERFLOW(x) \ - (((x) < 0) & ((unsigned long)(x) == 0-(unsigned long)(x))) -""") diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py index c9c30c5094814044e1b7be61ced11e78fd57263b..20526a84bb5eee7e7382497e5fc59f8976961d56 100644 --- a/Cython/Compiler/Main.py +++ b/Cython/Compiler/Main.py @@ -19,6 +19,7 @@ from . import Errors # conditional metaclass. These options are processed by CmdLine called from # main() in this file. # import Parsing +from .StringEncoding import EncodedString from .Scanning import PyrexScanner, FileSourceDescriptor from .Errors import PyrexError, CompileError, error, warning from .Symtab import ModuleScope @@ -76,7 +77,8 @@ class Context(object): self.cpp = cpp self.options = options - self.pxds = {} # full name -> node tree + self.pxds = {} # full name -> node tree + self._interned = {} # (type(value), value, *key_args) -> interned_value standard_include_path = os.path.abspath(os.path.normpath( os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes'))) @@ -93,6 +95,27 @@ class Context(object): self.future_directives.update([print_function, unicode_literals, absolute_import, division]) self.modules['builtins'] = self.modules['__builtin__'] + def intern_ustring(self, value, encoding=None): + key = (EncodedString, value, encoding) + try: + return self._interned[key] + except KeyError: + pass + value = EncodedString(value) + if encoding: + value.encoding = encoding + self._interned[key] = value + return value + + def intern_value(self, value, *key): + key = (type(value), value) + key + try: + return self._interned[key] + except KeyError: + pass + self._interned[key] = value + return value + # pipeline creation functions can now be found in Pipeline.py def process_pxd(self, source_desc, scope, module_name): @@ -110,7 +133,8 @@ class Context(object): def nonfatal_error(self, exc): return Errors.report_error(exc) - def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, check_module_name=True): + def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, + absolute_fallback=True): # Finds and returns the module scope corresponding to # the given relative or absolute module name. If this # is the first time the module has been requested, finds @@ -125,25 +149,39 @@ class Context(object): scope = None pxd_pathname = None - if check_module_name and not module_name_pattern.match(module_name): - if pos is None: - pos = (module_name, 0, 0) - raise CompileError(pos, "'%s' is not a valid module name" % module_name) + if relative_to: + if module_name: + # from .module import ... + qualified_name = relative_to.qualify_name(module_name) + else: + # from . import ... + qualified_name = relative_to.qualified_name + scope = relative_to + relative_to = None + else: + qualified_name = module_name + + if not module_name_pattern.match(qualified_name): + raise CompileError(pos or (module_name, 0, 0), + "'%s' is not a valid module name" % module_name) + if relative_to: if debug_find_module: print("...trying relative import") scope = relative_to.lookup_submodule(module_name) if not scope: - qualified_name = relative_to.qualify_name(module_name) pxd_pathname = self.find_pxd_file(qualified_name, pos) if pxd_pathname: scope = relative_to.find_submodule(module_name) if not scope: if debug_find_module: print("...trying absolute import") + if absolute_fallback: + qualified_name = module_name scope = self - for name in module_name.split("."): + for name in qualified_name.split("."): scope = scope.find_submodule(name) + if debug_find_module: print("...scope = %s" % scope) if not scope.pxd_file_loaded: @@ -153,15 +191,15 @@ class Context(object): if not pxd_pathname: if debug_find_module: print("...looking for pxd file") - pxd_pathname = self.find_pxd_file(module_name, pos) + pxd_pathname = self.find_pxd_file(qualified_name, pos) if debug_find_module: print("......found %s" % pxd_pathname) if not pxd_pathname and need_pxd: - package_pathname = self.search_include_directories(module_name, ".py", pos) + package_pathname = self.search_include_directories(qualified_name, ".py", pos) if package_pathname and package_pathname.endswith('__init__.py'): pass else: - error(pos, "'%s.pxd' not found" % module_name.replace('.', os.sep)) + error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep)) if pxd_pathname: try: if debug_find_module: @@ -170,7 +208,7 @@ class Context(object): if not pxd_pathname.endswith(rel_path): rel_path = pxd_pathname # safety measure to prevent printing incorrect paths source_desc = FileSourceDescriptor(pxd_pathname, rel_path) - err, result = self.process_pxd(source_desc, scope, module_name) + err, result = self.process_pxd(source_desc, scope, qualified_name) if err: raise err (pxd_codenodes, pxd_scope) = result diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7a06009da6767ee4ad9d378910d7b12f67df3198..28ec86e7885ac1c2a3b83c035670a69cb459a6b0 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -571,26 +571,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("") code.putln("#define PY_SSIZE_T_CLEAN") - # sizeof(PyLongObject.ob_digit[0]) may have been determined dynamically - # at compile time in CPython, in which case we can't know the correct - # storage size for an installed system. We can rely on it only if - # pyconfig.h defines it statically, i.e. if it was set by "configure". - # Once we include "Python.h", it will come up with its own idea about - # a suitable value, which may or may not match the real one. - code.putln("#ifndef CYTHON_USE_PYLONG_INTERNALS") - code.putln("#ifdef PYLONG_BITS_IN_DIGIT") - # assume it's an incorrect left-over - code.putln("#define CYTHON_USE_PYLONG_INTERNALS 0") - code.putln("#else") - code.putln('#include "pyconfig.h"') - code.putln("#ifdef PYLONG_BITS_IN_DIGIT") - code.putln("#define CYTHON_USE_PYLONG_INTERNALS 1") - code.putln("#else") - code.putln("#define CYTHON_USE_PYLONG_INTERNALS 0") - code.putln("#endif") - code.putln("#endif") - code.putln("#endif") - for filename in env.python_include_files: code.putln('#include "%s"' % filename) code.putln("#ifndef Py_PYTHON_H") @@ -1982,7 +1962,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): "};") def generate_import_star(self, env, code): - env.use_utility_code(streq_utility_code) + env.use_utility_code(UtilityCode.load_cached("CStringEquals", "StringTools.c")) code.putln() code.enter_cfunc_scope() # as we need labels code.putln("static int %s(PyObject *o, PyObject* py_name, char *name) {" % Naming.import_star_set) @@ -2044,8 +2024,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("bad:") code.putln("return -1;") code.putln("}") - code.putln(import_star_utility_code) - code.exit_cfunc_scope() # done with labels + code.putln("") + code.putln(UtilityCode.load_cached("ImportStar", "ImportExport.c").impl) + code.exit_cfunc_scope() # done with labels def generate_module_init_func(self, imported_modules, env, code): code.enter_cfunc_scope() @@ -2170,6 +2151,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("/*--- Execution code ---*/") code.mark_pos(None) + code.putln("#ifdef __Pyx_Generator_USED") + code.put_error_if_neg(self.pos, "__Pyx_patch_abc()") + code.putln("#endif") + if profile or linetrace: code.put_trace_call(header3, self.pos, nogil=not code.funcstate.gil_owned) code.funcstate.can_trace = True @@ -2358,12 +2343,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): wmain = "wmain" else: wmain = Options.embed + main_method = UtilityCode.load_cached("MainFunction", "Embed.c") code.globalstate.use_utility_code( main_method.specialize( - module_name = env.module_name, - module_is_main = module_is_main, - main_method = Options.embed, - wmain_method = wmain)) + module_name=env.module_name, + module_is_main=module_is_main, + main_method=Options.embed, + wmain_method=wmain)) def generate_pymoduledef_struct(self, env, code): if env.doc: @@ -2829,140 +2815,7 @@ def generate_cfunction_declaration(entry, env, code, definition): # #------------------------------------------------------------------------------------ -streq_utility_code = UtilityCode( -proto = """ -static CYTHON_INLINE int __Pyx_StrEq(const char *, const char *); /*proto*/ -""", -impl = """ -static CYTHON_INLINE int __Pyx_StrEq(const char *s1, const char *s2) { - while (*s1 != '\\0' && *s1 == *s2) { s1++; s2++; } - return *s1 == *s2; -} -""") - -#------------------------------------------------------------------------------------ - -import_star_utility_code = """ - -/* import_all_from is an unexposed function from ceval.c */ - -static int -__Pyx_import_all_from(PyObject *locals, PyObject *v) -{ - PyObject *all = PyObject_GetAttrString(v, "__all__"); - PyObject *dict, *name, *value; - int skip_leading_underscores = 0; - int pos, err; - - if (all == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) - return -1; /* Unexpected error */ - PyErr_Clear(); - dict = PyObject_GetAttrString(v, "__dict__"); - if (dict == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) - return -1; - PyErr_SetString(PyExc_ImportError, - "from-import-* object has no __dict__ and no __all__"); - return -1; - } -#if PY_MAJOR_VERSION < 3 - all = PyObject_CallMethod(dict, (char *)"keys", NULL); -#else - all = PyMapping_Keys(dict); -#endif - Py_DECREF(dict); - if (all == NULL) - return -1; - skip_leading_underscores = 1; - } - - for (pos = 0, err = 0; ; pos++) { - name = PySequence_GetItem(all, pos); - if (name == NULL) { - if (!PyErr_ExceptionMatches(PyExc_IndexError)) - err = -1; - else - PyErr_Clear(); - break; - } - if (skip_leading_underscores && -#if PY_MAJOR_VERSION < 3 - PyString_Check(name) && - PyString_AS_STRING(name)[0] == '_') -#else - PyUnicode_Check(name) && - PyUnicode_AS_UNICODE(name)[0] == '_') -#endif - { - Py_DECREF(name); - continue; - } - value = PyObject_GetAttr(v, name); - if (value == NULL) - err = -1; - else if (PyDict_CheckExact(locals)) - err = PyDict_SetItem(locals, name, value); - else - err = PyObject_SetItem(locals, name, value); - Py_DECREF(name); - Py_XDECREF(value); - if (err != 0) - break; - } - Py_DECREF(all); - return err; -} - - -static int %(IMPORT_STAR)s(PyObject* m) { - - int i; - int ret = -1; - char* s; - PyObject *locals = 0; - PyObject *list = 0; -#if PY_MAJOR_VERSION >= 3 - PyObject *utf8_name = 0; -#endif - PyObject *name; - PyObject *item; - - locals = PyDict_New(); if (!locals) goto bad; - if (__Pyx_import_all_from(locals, m) < 0) goto bad; - list = PyDict_Items(locals); if (!list) goto bad; - - for(i=0; i<PyList_GET_SIZE(list); i++) { - name = PyTuple_GET_ITEM(PyList_GET_ITEM(list, i), 0); - item = PyTuple_GET_ITEM(PyList_GET_ITEM(list, i), 1); -#if PY_MAJOR_VERSION >= 3 - utf8_name = PyUnicode_AsUTF8String(name); - if (!utf8_name) goto bad; - s = PyBytes_AS_STRING(utf8_name); - if (%(IMPORT_STAR_SET)s(item, name, s) < 0) goto bad; - Py_DECREF(utf8_name); utf8_name = 0; -#else - s = PyString_AsString(name); - if (!s) goto bad; - if (%(IMPORT_STAR_SET)s(item, name, s) < 0) goto bad; -#endif - } - ret = 0; - -bad: - Py_XDECREF(locals); - Py_XDECREF(list); -#if PY_MAJOR_VERSION >= 3 - Py_XDECREF(utf8_name); -#endif - return ret; -} -""" % {'IMPORT_STAR' : Naming.import_star, - 'IMPORT_STAR_SET' : Naming.import_star_set } - -refnanny_utility_code = UtilityCode.load_cached("Refnanny", "ModuleSetupCode.c") - -main_method = UtilityCode.load("MainFunction", "Embed.c") +refnanny_utility_code = UtilityCode.load("Refnanny", "ModuleSetupCode.c") packed_struct_utility_code = UtilityCode(proto=""" #if defined(__GNUC__) diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index 6539b3471ab69230e72746c01f8d42e2b3990beb..6a40ae123455accdd6a1afffb65bdbcb76f04e9d 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -61,6 +61,7 @@ interned_prefixes = { 'codeobj': pyrex_prefix + "codeobj_", 'slice': pyrex_prefix + "slice_", 'ustring': pyrex_prefix + "ustring_", + 'umethod': pyrex_prefix + "umethod_", } ctuple_type_prefix = pyrex_prefix + "ctuple_" diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index af985683d3f2bb0ddc5cf97f715d170bd4cbefd3..13134036979739ae80b843a0c5f981016d8d44a1 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1180,10 +1180,9 @@ class CTupleBaseTypeNode(CBaseTypeNode): error(c.pos, "Tuple types can't (yet) contain Python objects.") return error_type component_types.append(type) - type = PyrexTypes.c_tuple_type(component_types) - entry = env.declare_tuple_type(self.pos, type) + entry = env.declare_tuple_type(self.pos, component_types) entry.used = True - return type + return entry.type class FusedTypeNode(CBaseTypeNode): @@ -1829,7 +1828,17 @@ class FuncDefNode(StatNode, BlockNode): code.put_release_ensured_gil() # FIXME: what if the error return value is a Python value? - code.putln("return %s;" % self.error_value()) + err_val = self.error_value() + if err_val is None: + if not self.caller_will_check_exceptions(): + warning(self.entry.pos, + "Unraisable exception in function '%s'." % + self.entry.qualified_name, 0) + code.put_unraisable(self.entry.qualified_name, lenv.nogil) + #if self.return_type.is_void: + code.putln("return;") + else: + code.putln("return %s;" % err_val) code.putln("}") code.put_gotref(Naming.cur_scope_cname) # Note that it is unsafe to decref the scope at this point. @@ -1863,20 +1872,21 @@ class FuncDefNode(StatNode, BlockNode): is_cdef = isinstance(self, CFuncDefNode) for entry in lenv.arg_entries: if entry.type.is_pyobject: - if ((acquire_gil or len(entry.cf_assignments) > 1) and - not entry.in_closure): + if (acquire_gil or len(entry.cf_assignments) > 1) and not entry.in_closure: code.put_var_incref(entry) # Note: defaults are always incref-ed. For def functions, we # we aquire arguments from object converstion, so we have # new references. If we are a cdef function, we need to # incref our arguments - elif (is_cdef and entry.type.is_memoryviewslice and - len(entry.cf_assignments) > 1): + elif is_cdef and entry.type.is_memoryviewslice and len(entry.cf_assignments) > 1: code.put_incref_memoryviewslice(entry.cname, have_gil=code.funcstate.gil_owned) for entry in lenv.var_entries: if entry.is_arg and len(entry.cf_assignments) > 1: - code.put_var_incref(entry) + if entry.xdecref_cleanup: + code.put_var_xincref(entry) + else: + code.put_var_incref(entry) # ----- Initialise local buffer auxiliary variables for entry in lenv.var_entries + lenv.arg_entries: @@ -2017,7 +2027,10 @@ class FuncDefNode(StatNode, BlockNode): have_gil=not lenv.nogil) elif entry.type.is_pyobject: if not entry.is_arg or len(entry.cf_assignments) > 1: - code.put_var_decref(entry) + if entry.xdecref_cleanup: + code.put_var_xdecref(entry) + else: + code.put_var_decref(entry) # Decref any increfed args for entry in lenv.arg_entries: @@ -3131,6 +3144,15 @@ class DefNodeWrapper(FuncDefNode): if not arg.hdr_type.create_to_py_utility_code(env): pass # will fail later + if self.starstar_arg and not self.starstar_arg.entry.cf_used: + # we will set the kwargs argument to NULL instead of a new dict + # and must therefore correct the control flow state + entry = self.starstar_arg.entry + entry.xdecref_cleanup = 1 + for ass in entry.cf_assignments: + if not ass.is_arg and ass.lhs.is_name: + ass.lhs.cf_maybe_null = True + def signature_has_nongeneric_args(self): argcount = len(self.args) if argcount == 0 or ( @@ -3380,7 +3402,7 @@ class DefNodeWrapper(FuncDefNode): code.putln("}") if self.starstar_arg: - if self.star_arg: + if self.star_arg or not self.starstar_arg.entry.cf_used: kwarg_check = "unlikely(%s)" % Naming.kwds_cname else: kwarg_check = "%s" % Naming.kwds_cname @@ -3394,15 +3416,28 @@ class DefNodeWrapper(FuncDefNode): kwarg_check, Naming.kwds_cname, self.name, bool(self.starstar_arg), self.error_value())) - if self.starstar_arg: - code.putln("%s = (%s) ? PyDict_Copy(%s) : PyDict_New();" % ( - self.starstar_arg.entry.cname, - Naming.kwds_cname, - Naming.kwds_cname)) - code.putln("if (unlikely(!%s)) return %s;" % ( - self.starstar_arg.entry.cname, self.error_value())) - self.starstar_arg.entry.xdecref_cleanup = 0 - code.put_gotref(self.starstar_arg.entry.cname) + if self.starstar_arg and self.starstar_arg.entry.cf_used: + if all(ref.node.allow_null for ref in self.starstar_arg.entry.cf_references): + code.putln("if (%s) {" % kwarg_check) + code.putln("%s = PyDict_Copy(%s); if (unlikely(!%s)) return %s;" % ( + self.starstar_arg.entry.cname, + Naming.kwds_cname, + self.starstar_arg.entry.cname, + self.error_value())) + code.put_gotref(self.starstar_arg.entry.cname) + code.putln("} else {") + code.putln("%s = NULL;" % (self.starstar_arg.entry.cname,)) + code.putln("}") + self.starstar_arg.entry.xdecref_cleanup = 1 + else: + code.put("%s = (%s) ? PyDict_Copy(%s) : PyDict_New(); " % ( + self.starstar_arg.entry.cname, + Naming.kwds_cname, + Naming.kwds_cname)) + code.putln("if (unlikely(!%s)) return %s;" % ( + self.starstar_arg.entry.cname, self.error_value())) + self.starstar_arg.entry.xdecref_cleanup = 0 + code.put_gotref(self.starstar_arg.entry.cname) if self.self_in_stararg and not self.target.is_staticmethod: # need to create a new tuple with 'self' inserted as first item @@ -3410,9 +3445,9 @@ class DefNodeWrapper(FuncDefNode): self.star_arg.entry.cname, Naming.args_cname, self.star_arg.entry.cname)) - if self.starstar_arg: + if self.starstar_arg and self.starstar_arg.entry.cf_used: code.putln("{") - code.put_decref_clear(self.starstar_arg.entry.cname, py_object_type) + code.put_xdecref_clear(self.starstar_arg.entry.cname, py_object_type) code.putln("return %s;" % self.error_value()) code.putln("}") else: @@ -4132,9 +4167,11 @@ class OverrideCheckNode(StatNode): code.funcstate.release_temp(func_node_temp) code.putln("}") + class ClassDefNode(StatNode, BlockNode): pass + class PyClassDefNode(ClassDefNode): # A Python class definition. # @@ -4160,7 +4197,7 @@ class PyClassDefNode(ClassDefNode): mkw = None def __init__(self, pos, name, bases, doc, body, decorators=None, - keyword_args=None, starstar_arg=None, force_py3_semantics=False): + keyword_args=None, force_py3_semantics=False): StatNode.__init__(self, pos) self.name = name self.doc = doc @@ -4175,31 +4212,30 @@ class PyClassDefNode(ClassDefNode): doc_node = None allow_py2_metaclass = not force_py3_semantics - if keyword_args or starstar_arg: + if keyword_args: allow_py2_metaclass = False self.is_py3_style_class = True - if keyword_args and not starstar_arg: - for i, item in list(enumerate(keyword_args.key_value_pairs))[::-1]: - if item.key.value == 'metaclass': - if self.metaclass is not None: - error(item.pos, "keyword argument 'metaclass' passed multiple times") - # special case: we already know the metaclass, - # so we don't need to do the "build kwargs, - # find metaclass" dance at runtime - self.metaclass = item.value - del keyword_args.key_value_pairs[i] - if starstar_arg: - self.mkw = ExprNodes.KeywordArgsNode( - pos, keyword_args=keyword_args and keyword_args.key_value_pairs or [], - starstar_arg=starstar_arg) - elif keyword_args.key_value_pairs: - self.mkw = keyword_args + if keyword_args.is_dict_literal: + if keyword_args.key_value_pairs: + for i, item in list(enumerate(keyword_args.key_value_pairs))[::-1]: + if item.key.value == 'metaclass': + if self.metaclass is not None: + error(item.pos, "keyword argument 'metaclass' passed multiple times") + # special case: we already know the metaclass, + # so we don't need to do the "build kwargs, + # find metaclass" dance at runtime + self.metaclass = item.value + del keyword_args.key_value_pairs[i] + self.mkw = keyword_args + else: + assert self.metaclass is not None else: - assert self.metaclass is not None + # MergedDictNode + self.mkw = ExprNodes.ProxyNode(keyword_args) if force_py3_semantics or self.bases or self.mkw or self.metaclass: if self.metaclass is None: - if starstar_arg: + if keyword_args and not keyword_args.is_dict_literal: # **kwargs may contain 'metaclass' arg mkdict = self.mkw else: @@ -5475,10 +5511,11 @@ class ReturnStatNode(StatNode): have_gil=self.in_nogil_context) elif self.in_generator: # return value == raise StopIteration(value), but uncatchable - code.putln( - "%s = NULL; PyErr_SetObject(PyExc_StopIteration, %s);" % ( - Naming.retval_cname, - self.value.result_as(self.return_type))) + code.globalstate.use_utility_code( + UtilityCode.load_cached("ReturnWithStopIteration", "Generator.c")) + code.putln("%s = NULL; __Pyx_ReturnWithStopIteration(%s);" % ( + Naming.retval_cname, + self.value.py_result())) self.value.generate_disposal_code(code) else: self.value.make_owned_reference(code) @@ -5490,7 +5527,10 @@ class ReturnStatNode(StatNode): self.value.free_temps(code) else: if self.return_type.is_pyobject: - code.put_init_to_py_none(Naming.retval_cname, self.return_type) + if self.in_generator: + code.putln("%s = NULL;" % Naming.retval_cname) + else: + code.put_init_to_py_none(Naming.retval_cname, self.return_type) elif self.return_type.is_returncode: self.put_return(code, self.return_type.default_value) @@ -6556,7 +6596,7 @@ class TryExceptStatNode(StatNode): else: # try block cannot raise exceptions, but we had to allocate the temps above, # so just keep the C compiler from complaining about them being unused - save_exc.putln("if (%s); else {/*mark used*/};" % '||'.join(exc_save_vars)) + save_exc.putln("if (%s); else {/*mark used*/}" % '||'.join(exc_save_vars)) def restore_saved_exception(): pass @@ -7162,6 +7202,14 @@ utility_code_for_cimports = { } +utility_code_for_imports = { + # utility code used when special modules are imported. + # TODO: Consider a generic user-level mechanism for importing + 'asyncio': ("__Pyx_patch_asyncio", "PatchAsyncIO", "Generator.c"), + 'inspect': ("__Pyx_patch_inspect", "PatchInspect", "Generator.c"), +} + + class CImportStatNode(StatNode): # cimport statement # @@ -7224,6 +7272,7 @@ class FromCImportStatNode(StatNode): return if self.relative_level and self.relative_level > env.qualified_name.count('.'): error(self.pos, "relative cimport beyond main package is not allowed") + return module_scope = env.find_module(self.module_name, self.pos, relative_level=self.relative_level) module_name = module_scope.qualified_name env.add_imported_module(module_scope) @@ -7244,7 +7293,8 @@ class FromCImportStatNode(StatNode): elif kind == 'class': entry = module_scope.declare_c_class(name, pos=pos, module_name=module_name) else: - submodule_scope = env.context.find_module(name, relative_to=module_scope, pos=self.pos) + submodule_scope = env.context.find_module( + name, relative_to=module_scope, pos=self.pos, absolute_fallback=False) if submodule_scope.parent_module is module_scope: env.declare_module(as_name or name, submodule_scope, self.pos) else: diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 260e435d245e9391a0726bad0e6936312fc1f146..297fc510841256325c7319471d23999d63fb0c0a 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -1422,6 +1422,31 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): stop=stop, step=step or ExprNodes.NoneNode(node.pos)) + def _handle_simple_function_ord(self, node, pos_args): + """Unpack ord('X'). + """ + if len(pos_args) != 1: + return node + arg = pos_args[0] + if isinstance(arg, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)): + if len(arg.value) == 1: + return ExprNodes.IntNode( + arg.pos, type=PyrexTypes.c_long_type, + value=str(ord(arg.value)), + constant_result=ord(arg.value) + ) + elif isinstance(arg, ExprNodes.StringNode): + if arg.unicode_value and len(arg.unicode_value) == 1 \ + and ord(arg.unicode_value) <= 255: # Py2/3 portability + return ExprNodes.IntNode( + arg.pos, type=PyrexTypes.c_int_type, + value=str(ord(arg.unicode_value)), + constant_result=ord(arg.unicode_value) + ) + return node + + # sequence processing + class YieldNodeCollector(Visitor.TreeVisitor): def __init__(self): Visitor.TreeVisitor.__init__(self) @@ -1632,7 +1657,7 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): yield_expression, yield_stat_node = self._find_single_yield_expression(loop_node) if yield_expression is None: return node - else: # ComprehensionNode + else: # ComprehensionNode yield_stat_node = gen_expr_node.append yield_expression = yield_stat_node.expr try: @@ -1712,6 +1737,8 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): return last_result + # builtin type creation + def _DISABLED_handle_simple_function_tuple(self, node, pos_args): if not pos_args: return ExprNodes.TupleNode(node.pos, args=[], constant_result=()) @@ -2297,14 +2324,14 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, exception_value="-1") _map_to_capi_len_function = { - Builtin.unicode_type : "__Pyx_PyUnicode_GET_LENGTH", - Builtin.bytes_type : "PyBytes_GET_SIZE", - Builtin.list_type : "PyList_GET_SIZE", - Builtin.tuple_type : "PyTuple_GET_SIZE", - Builtin.dict_type : "PyDict_Size", - Builtin.set_type : "PySet_Size", - Builtin.frozenset_type : "__Pyx_PyFrozenSet_Size", - }.get + Builtin.unicode_type: "__Pyx_PyUnicode_GET_LENGTH", + Builtin.bytes_type: "PyBytes_GET_SIZE", + Builtin.list_type: "PyList_GET_SIZE", + Builtin.tuple_type: "PyTuple_GET_SIZE", + Builtin.set_type: "PySet_GET_SIZE", + Builtin.frozenset_type: "PySet_GET_SIZE", + Builtin.dict_type: "PyDict_Size", + }.get _ext_types_with_pysize = set(["cpython.array.array"]) @@ -2452,7 +2479,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, if isinstance(arg, ExprNodes.CoerceToPyTypeNode): if arg.arg.type.is_unicode_char: return ExprNodes.TypecastNode( - arg.pos, operand=arg.arg, type=PyrexTypes.c_int_type + arg.pos, operand=arg.arg, type=PyrexTypes.c_long_type ).coerce_to(node.type, self.current_env()) elif isinstance(arg, ExprNodes.UnicodeNode): if len(arg.value) == 1: @@ -2640,7 +2667,8 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, PyObject_PopIndex_func_type = PyrexTypes.CFuncType( PyrexTypes.py_object_type, [ PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None), - PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_py_ssize_t_type, None), + PyrexTypes.CFuncTypeArg("py_index", PyrexTypes.py_object_type, None), + PyrexTypes.CFuncTypeArg("c_index", PyrexTypes.c_py_ssize_t_type, None), PyrexTypes.CFuncTypeArg("is_signed", PyrexTypes.c_int_type, None), ], has_varargs=True) # to fake the additional macro args that lack a proper C type @@ -2675,14 +2703,23 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ) elif len(args) == 2: index = unwrap_coerced_node(args[1]) + py_index = ExprNodes.NoneNode(index.pos) orig_index_type = index.type if not index.type.is_int: - if is_list or isinstance(index, ExprNodes.IntNode): + if isinstance(index, ExprNodes.IntNode): + py_index = index.coerce_to_pyobject(self.current_env()) + index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env()) + elif is_list: + if index.type.is_pyobject: + py_index = index.coerce_to_simple(self.current_env()) + index = ExprNodes.CloneNode(py_index) index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env()) else: return node elif not PyrexTypes.numeric_type_fits(index.type, PyrexTypes.c_py_ssize_t_type): return node + elif isinstance(index, ExprNodes.IntNode): + py_index = index.coerce_to_pyobject(self.current_env()) # real type might still be larger at runtime if not orig_index_type.is_int: orig_index_type = index.type @@ -2694,7 +2731,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, return ExprNodes.PythonCapiCallNode( node.pos, "__Pyx_Py%s_PopIndex" % type_name, self.PyObject_PopIndex_func_type, - args=[obj, index, + args=[obj, py_index, index, ExprNodes.IntNode(index.pos, value=str(orig_index_type.signed and 1 or 0), constant_result=orig_index_type.signed and 1 or 0, type=PyrexTypes.c_int_type), @@ -2802,38 +2839,110 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, def _handle_simple_method_object___sub__(self, node, function, args, is_unbound_method): return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method) + def _handle_simple_method_object___eq__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Eq', node, function, args, is_unbound_method) + + def _handle_simple_method_object___neq__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Ne', node, function, args, is_unbound_method) + + def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('And', node, function, args, is_unbound_method) + + def _handle_simple_method_object___or__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Or', node, function, args, is_unbound_method) + + def _handle_simple_method_object___xor__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Xor', node, function, args, is_unbound_method) + + def _handle_simple_method_object___rshift__(self, node, function, args, is_unbound_method): + if len(args) != 2 or not isinstance(args[1], ExprNodes.IntNode): + return node + if not args[1].has_constant_result() or not (1 <= args[1].constant_result <= 63): + return node + return self._optimise_num_binop('Rshift', node, function, args, is_unbound_method) + + def _handle_simple_method_object___mod__(self, node, function, args, is_unbound_method): + return self._optimise_num_div('Remainder', node, function, args, is_unbound_method) + + def _handle_simple_method_object___floordiv__(self, node, function, args, is_unbound_method): + return self._optimise_num_div('FloorDivide', node, function, args, is_unbound_method) + + def _handle_simple_method_object___truediv__(self, node, function, args, is_unbound_method): + return self._optimise_num_div('TrueDivide', node, function, args, is_unbound_method) + + def _handle_simple_method_object___div__(self, node, function, args, is_unbound_method): + return self._optimise_num_div('Divide', node, function, args, is_unbound_method) + + def _optimise_num_div(self, operator, node, function, args, is_unbound_method): + if len(args) != 2 or not args[1].has_constant_result() or args[1].constant_result == 0: + return node + if isinstance(args[1], ExprNodes.IntNode): + if not (-2**30 <= args[1].constant_result <= 2**30): + return node + elif isinstance(args[1], ExprNodes.FloatNode): + if not (-2**53 <= args[1].constant_result <= 2**53): + return node + else: + return node + return self._optimise_num_binop(operator, node, function, args, is_unbound_method) + def _handle_simple_method_float___add__(self, node, function, args, is_unbound_method): return self._optimise_num_binop('Add', node, function, args, is_unbound_method) def _handle_simple_method_float___sub__(self, node, function, args, is_unbound_method): return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method) + def _handle_simple_method_float___truediv__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('TrueDivide', node, function, args, is_unbound_method) + + def _handle_simple_method_float___div__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Divide', node, function, args, is_unbound_method) + + def _handle_simple_method_float___mod__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Remainder', node, function, args, is_unbound_method) + + def _handle_simple_method_float___eq__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Eq', node, function, args, is_unbound_method) + + def _handle_simple_method_float___neq__(self, node, function, args, is_unbound_method): + return self._optimise_num_binop('Ne', node, function, args, is_unbound_method) + def _optimise_num_binop(self, operator, node, function, args, is_unbound_method): """ - Optimise '+' / '-' operator for (likely) float or small integer operations. + Optimise math operators for (likely) float or small integer operations. """ if len(args) != 2: return node if not node.type.is_pyobject: return node - # when adding IntNode/FloatNode to something else, assume other operand is also numeric + # When adding IntNode/FloatNode to something else, assume other operand is also numeric. + # Prefer constants on RHS as they allows better size control for some operators. num_nodes = (ExprNodes.IntNode, ExprNodes.FloatNode) - if isinstance(args[0], num_nodes): - if args[1].type is not PyrexTypes.py_object_type: - return node - numval = args[0] - arg_order = 'CObj' - elif isinstance(args[1], num_nodes): + if isinstance(args[1], num_nodes): if args[0].type is not PyrexTypes.py_object_type: return node numval = args[1] arg_order = 'ObjC' + elif isinstance(args[0], num_nodes): + if args[1].type is not PyrexTypes.py_object_type: + return node + numval = args[0] + arg_order = 'CObj' else: return node + if not numval.has_constant_result(): + return node + is_float = isinstance(numval, ExprNodes.FloatNode) - if not numval.has_constant_result() or (not is_float and abs(numval.constant_result) > 2**30): + if is_float: + if operator not in ('Add', 'Subtract', 'Remainder', 'TrueDivide', 'Divide', 'Eq', 'Ne'): + return node + elif operator == 'Divide': + # mixed old-/new-style division is not currently optimised for integers + return node + elif abs(numval.constant_result) > 2**30: return node args = list(args) @@ -3724,6 +3833,93 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): sequence_node.mult_factor = factor return sequence_node + def visit_MergedDictNode(self, node): + """Unpack **args in place if we can.""" + self.visitchildren(node) + args = [] + items = [] + + def add(arg): + if arg.is_dict_literal: + if items: + items[0].key_value_pairs.extend(arg.key_value_pairs) + else: + items.append(arg) + elif isinstance(arg, ExprNodes.MergedDictNode): + for child_arg in arg.keyword_args: + add(child_arg) + else: + if items: + args.append(items[0]) + del items[:] + args.append(arg) + + for arg in node.keyword_args: + add(arg) + if items: + args.append(items[0]) + + if len(args) == 1: + arg = args[0] + if arg.is_dict_literal or isinstance(arg, ExprNodes.MergedDictNode): + return arg + node.keyword_args[:] = args + self._calculate_const(node) + return node + + def visit_MergedSequenceNode(self, node): + """Unpack *args in place if we can.""" + self.visitchildren(node) + + is_set = node.type is Builtin.set_type + args = [] + values = [] + + def add(arg): + if (is_set and arg.is_set_literal) or (arg.is_sequence_constructor and not arg.mult_factor): + if values: + values[0].args.extend(arg.args) + else: + values.append(arg) + elif isinstance(arg, ExprNodes.MergedSequenceNode): + for child_arg in arg.args: + add(child_arg) + else: + if values: + args.append(values[0]) + del values[:] + args.append(arg) + + for arg in node.args: + add(arg) + if values: + args.append(values[0]) + + if len(args) == 1: + arg = args[0] + if ((is_set and arg.is_set_literal) or + (arg.is_sequence_constructor and arg.type is node.type) or + isinstance(arg, ExprNodes.MergedSequenceNode)): + return arg + node.args[:] = args + self._calculate_const(node) + return node + + def visit_SequenceNode(self, node): + """Unpack *args in place if we can.""" + self.visitchildren(node) + args = [] + for arg in node.args: + if not arg.is_starred: + args.append(arg) + elif arg.target.is_sequence_constructor and not arg.target.mult_factor: + args.extend(arg.target.args) + else: + args.append(arg) + node.args[:] = args + self._calculate_const(node) + return node + def visit_PrimaryCmpNode(self, node): # calculate constant partial results in the comparison cascade self.visitchildren(node, ['operand1']) diff --git a/Cython/Compiler/Parsing.pxd b/Cython/Compiler/Parsing.pxd index 8f87f290716409dc5cb8f09c293bb734660a1779..9a96d1acc659198ad1b8006c5950aa4a147ab30d 100644 --- a/Cython/Compiler/Parsing.pxd +++ b/Cython/Compiler/Parsing.pxd @@ -48,7 +48,7 @@ cdef p_power(PyrexScanner s) cdef p_new_expr(PyrexScanner s) cdef p_trailer(PyrexScanner s, node1) cdef p_call_parse_args(PyrexScanner s, bint allow_genexp = *) -cdef p_call_build_packed_args(pos, positional_args, keyword_args, star_arg, starstar_arg) +cdef p_call_build_packed_args(pos, positional_args, keyword_args) cdef p_call(PyrexScanner s, function) cdef p_index(PyrexScanner s, base) cdef tuple p_subscript_list(PyrexScanner s) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 962adcbcfe4c3c0206db86e590cb7c61b6177385..06259cd4d53116dd1ce59c6d138f920e0fa529fd 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -12,10 +12,12 @@ cython.declare(Nodes=object, ExprNodes=object, EncodedString=object, FileSourceDescriptor=object, lookup_unicodechar=object, Future=object, Options=object, error=object, warning=object, Builtin=object, ModuleNode=object, Utils=object, - re=object, _unicode=object, _bytes=object) + re=object, _unicode=object, _bytes=object, + partial=object, reduce=object) import re from unicodedata import lookup as lookup_unicodechar +from functools import partial, reduce from .Scanning import PyrexScanner, FileSourceDescriptor from . import Nodes @@ -53,7 +55,7 @@ class Ctx(object): d.update(kwds) return ctx -def p_ident(s, message = "Expected an identifier"): +def p_ident(s, message="Expected an identifier"): if s.sy == 'IDENT': name = s.systring s.next() @@ -208,7 +210,7 @@ def p_starred_expr(s): starred = False expr = p_bit_expr(s) if starred: - expr = ExprNodes.StarredTargetNode(pos, expr) + expr = ExprNodes.StarredUnpackingNode(pos, expr) return expr def p_cascaded_cmp(s): @@ -405,28 +407,39 @@ def p_trailer(s, node1): return p_index(s, node1) else: # s.sy == '.' s.next() - name = EncodedString( p_ident(s) ) + name = p_ident(s) return ExprNodes.AttributeNode(pos, - obj = node1, attribute = name) + obj=node1, attribute=name) + # arglist: argument (',' argument)* [','] # argument: [test '='] test # Really [keyword '='] test -def p_call_parse_args(s, allow_genexp = True): +# since PEP 448: +# argument: ( test [comp_for] | +# test '=' test | +# '**' expr | +# star_expr ) + +def p_call_parse_args(s, allow_genexp=True): # s.sy == '(' pos = s.position() s.next() positional_args = [] keyword_args = [] - star_arg = None - starstar_arg = None - while s.sy not in ('**', ')'): + starstar_seen = False + last_was_tuple_unpack = False + while s.sy != ')': if s.sy == '*': - if star_arg: - s.error("only one star-arg parameter allowed", - pos=s.position()) + if starstar_seen: + s.error("Non-keyword arg following keyword arg", pos=s.position()) + s.next() + positional_args.append(p_test(s)) + last_was_tuple_unpack = True + elif s.sy == '**': s.next() - star_arg = p_test(s) + keyword_args.append(p_test(s)) + starstar_seen = True else: arg = p_test(s) if s.sy == '=': @@ -434,80 +447,85 @@ def p_call_parse_args(s, allow_genexp = True): if not arg.is_name: s.error("Expected an identifier before '='", pos=arg.pos) - encoded_name = EncodedString(arg.name) + encoded_name = s.context.intern_ustring(arg.name) keyword = ExprNodes.IdentifierStringNode( arg.pos, value=encoded_name) arg = p_test(s) keyword_args.append((keyword, arg)) else: if keyword_args: - s.error("Non-keyword arg following keyword arg", - pos=arg.pos) - if star_arg: - s.error("Non-keyword arg following star-arg", - pos=arg.pos) - positional_args.append(arg) + s.error("Non-keyword arg following keyword arg", pos=arg.pos) + if positional_args and not last_was_tuple_unpack: + positional_args[-1].append(arg) + else: + positional_args.append([arg]) + last_was_tuple_unpack = False if s.sy != ',': break s.next() if s.sy == 'for': - if len(positional_args) == 1 and not star_arg: - positional_args = [ p_genexp(s, positional_args[0]) ] - elif s.sy == '**': - s.next() - starstar_arg = p_test(s) - if s.sy == ',': - s.next() # FIXME: this is actually not valid Python syntax + if not keyword_args and not last_was_tuple_unpack: + if len(positional_args) == 1 and len(positional_args[0]) == 1: + positional_args = [[p_genexp(s, positional_args[0][0])]] s.expect(')') - return positional_args, keyword_args, star_arg, starstar_arg + return positional_args or [[]], keyword_args -def p_call_build_packed_args(pos, positional_args, keyword_args, - star_arg, starstar_arg): - arg_tuple = None + +def p_call_build_packed_args(pos, positional_args, keyword_args): keyword_dict = None - if positional_args or not star_arg: - arg_tuple = ExprNodes.TupleNode(pos, - args = positional_args) - if star_arg: - star_arg_tuple = ExprNodes.AsTupleNode(pos, arg = star_arg) - if arg_tuple: - arg_tuple = ExprNodes.binop_node(pos, - operator = '+', operand1 = arg_tuple, - operand2 = star_arg_tuple) - else: - arg_tuple = star_arg_tuple - if keyword_args or starstar_arg: - keyword_args = [ExprNodes.DictItemNode(pos=key.pos, key=key, value=value) - for key, value in keyword_args] - if starstar_arg: - keyword_dict = ExprNodes.KeywordArgsNode( - pos, - starstar_arg = starstar_arg, - keyword_args = keyword_args) - else: - keyword_dict = ExprNodes.DictNode( - pos, key_value_pairs = keyword_args) + + subtuples = [ + ExprNodes.TupleNode(pos, args=arg) if isinstance(arg, list) else ExprNodes.AsTupleNode(pos, arg=arg) + for arg in positional_args + ] + # TODO: implement a faster way to join tuples than creating each one and adding them + arg_tuple = reduce(partial(ExprNodes.binop_node, pos, '+'), subtuples) + + if keyword_args: + kwargs = [] + dict_items = [] + for item in keyword_args: + if isinstance(item, tuple): + key, value = item + dict_items.append(ExprNodes.DictItemNode(pos=key.pos, key=key, value=value)) + elif item.is_dict_literal: + # unpack "**{a:b}" directly + dict_items.extend(item.key_value_pairs) + else: + if dict_items: + kwargs.append(ExprNodes.DictNode( + dict_items[0].pos, key_value_pairs=dict_items, reject_duplicates=True)) + dict_items = [] + kwargs.append(item) + + if dict_items: + kwargs.append(ExprNodes.DictNode( + dict_items[0].pos, key_value_pairs=dict_items, reject_duplicates=True)) + + if kwargs: + if len(kwargs) == 1 and kwargs[0].is_dict_literal: + # only simple keyword arguments found -> one dict + keyword_dict = kwargs[0] + else: + # at least one **kwargs + keyword_dict = ExprNodes.MergedDictNode(pos, keyword_args=kwargs) + return arg_tuple, keyword_dict + def p_call(s, function): # s.sy == '(' pos = s.position() + positional_args, keyword_args = p_call_parse_args(s) - positional_args, keyword_args, star_arg, starstar_arg = \ - p_call_parse_args(s) - - if not (keyword_args or star_arg or starstar_arg): - return ExprNodes.SimpleCallNode(pos, - function = function, - args = positional_args) + if not keyword_args and len(positional_args) == 1 and isinstance(positional_args[0], list): + return ExprNodes.SimpleCallNode(pos, function=function, args=positional_args[0]) else: - arg_tuple, keyword_dict = p_call_build_packed_args( - pos, positional_args, keyword_args, star_arg, starstar_arg) - return ExprNodes.GeneralCallNode(pos, - function = function, - positional_args = arg_tuple, - keyword_args = keyword_dict) + arg_tuple, keyword_dict = p_call_build_packed_args(pos, positional_args, keyword_args) + return ExprNodes.GeneralCallNode( + pos, function=function, positional_args=arg_tuple, keyword_args=keyword_dict) + #lambdef: 'lambda' [varargslist] ':' test @@ -643,7 +661,7 @@ def p_atom(s): else: return ExprNodes.StringNode(pos, value = bytes_value, unicode_value = unicode_value) elif sy == 'IDENT': - name = EncodedString( s.systring ) + name = s.systring s.next() if name == "None": return ExprNodes.NoneNode(pos) @@ -897,11 +915,13 @@ def p_string_literal(s, kind_override=None): s.next() return (kind, bytes_value, unicode_value) -# list_display ::= "[" [listmaker] "]" -# listmaker ::= expression ( comp_for | ( "," expression )* [","] ) + +# since PEP 448: +# list_display ::= "[" [listmaker] "]" +# listmaker ::= (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) # comp_iter ::= comp_for | comp_if -# comp_for ::= "for" expression_list "in" testlist [comp_iter] -# comp_if ::= "if" test [comp_iter] +# comp_for ::= "for" expression_list "in" testlist [comp_iter] +# comp_if ::= "if" test [comp_iter] def p_list_maker(s): # s.sy == '[' @@ -909,24 +929,29 @@ def p_list_maker(s): s.next() if s.sy == ']': s.expect(']') - return ExprNodes.ListNode(pos, args = []) - expr = p_test(s) + return ExprNodes.ListNode(pos, args=[]) + + expr = p_test_or_starred_expr(s) if s.sy == 'for': + if expr.is_starred: + s.error("iterable unpacking cannot be used in comprehension") append = ExprNodes.ComprehensionAppendNode(pos, expr=expr) loop = p_comp_for(s, append) s.expect(']') return ExprNodes.ComprehensionNode( - pos, loop=loop, append=append, type = Builtin.list_type, + pos, loop=loop, append=append, type=Builtin.list_type, # list comprehensions leak their loop variable in Py2 - has_local_scope = s.context.language_level >= 3) + has_local_scope=s.context.language_level >= 3) + + # (merged) list literal + if s.sy == ',': + s.next() + exprs = p_test_or_starred_expr_list(s, expr) else: - if s.sy == ',': - s.next() - exprs = p_simple_expr_list(s, expr) - else: - exprs = [expr] - s.expect(']') - return ExprNodes.ListNode(pos, args = exprs) + exprs = [expr] + s.expect(']') + return ExprNodes.ListNode(pos, args=exprs) + def p_comp_iter(s, body): if s.sy == 'for': @@ -955,7 +980,12 @@ def p_comp_if(s, body): body = p_comp_iter(s, body))], else_clause = None ) -#dictmaker: test ':' test (',' test ':' test)* [','] + +# since PEP 448: +#dictorsetmaker: ( ((test ':' test | '**' expr) +# (comp_for | (',' (test ':' test | '**' expr))* [','])) | +# ((test | star_expr) +# (comp_for | (',' (test | star_expr))* [','])) ) def p_dict_or_set_maker(s): # s.sy == '{' @@ -963,57 +993,108 @@ def p_dict_or_set_maker(s): s.next() if s.sy == '}': s.next() - return ExprNodes.DictNode(pos, key_value_pairs = []) - item = p_test(s) - if s.sy == ',' or s.sy == '}': - # set literal - values = [item] - while s.sy == ',': + return ExprNodes.DictNode(pos, key_value_pairs=[]) + + parts = [] + target_type = 0 + last_was_simple_item = False + while True: + if s.sy in ('*', '**'): + # merged set/dict literal + if target_type == 0: + target_type = 1 if s.sy == '*' else 2 # 'stars' + elif target_type != len(s.sy): + s.error("unexpected %sitem found in %s literal" % ( + s.sy, 'set' if target_type == 1 else 'dict')) + s.next() + if s.sy == '*': + s.error("expected expression, found '*'") + item = p_starred_expr(s) + parts.append(item) + last_was_simple_item = False + else: + item = p_test(s) + if target_type == 0: + target_type = 2 if s.sy == ':' else 1 # dict vs. set + if target_type == 2: + # dict literal + s.expect(':') + key = item + value = p_test(s) + item = ExprNodes.DictItemNode(key.pos, key=key, value=value) + if last_was_simple_item: + parts[-1].append(item) + else: + parts.append([item]) + last_was_simple_item = True + + if s.sy == ',': s.next() if s.sy == '}': break - values.append( p_test(s) ) - s.expect('}') - return ExprNodes.SetNode(pos, args=values) - elif s.sy == 'for': - # set comprehension - append = ExprNodes.ComprehensionAppendNode( - item.pos, expr=item) - loop = p_comp_for(s, append) - s.expect('}') - return ExprNodes.ComprehensionNode( - pos, loop=loop, append=append, type=Builtin.set_type) - elif s.sy == ':': - # dict literal or comprehension - key = item - s.next() - value = p_test(s) - if s.sy == 'for': - # dict comprehension - append = ExprNodes.DictComprehensionAppendNode( - item.pos, key_expr=key, value_expr=value) + else: + break + + if s.sy == 'for': + # dict/set comprehension + if len(parts) == 1 and isinstance(parts[0], list) and len(parts[0]) == 1: + item = parts[0][0] + if target_type == 2: + assert isinstance(item, ExprNodes.DictItemNode), type(item) + comprehension_type = Builtin.dict_type + append = ExprNodes.DictComprehensionAppendNode( + item.pos, key_expr=item.key, value_expr=item.value) + else: + comprehension_type = Builtin.set_type + append = ExprNodes.ComprehensionAppendNode(item.pos, expr=item) loop = p_comp_for(s, append) s.expect('}') - return ExprNodes.ComprehensionNode( - pos, loop=loop, append=append, type=Builtin.dict_type) + return ExprNodes.ComprehensionNode(pos, loop=loop, append=append, type=comprehension_type) else: - # dict literal - items = [ExprNodes.DictItemNode(key.pos, key=key, value=value)] - while s.sy == ',': - s.next() - if s.sy == '}': - break - key = p_test(s) - s.expect(':') - value = p_test(s) - items.append( - ExprNodes.DictItemNode(key.pos, key=key, value=value)) - s.expect('}') - return ExprNodes.DictNode(pos, key_value_pairs=items) - else: - # raise an error - s.expect('}') - return ExprNodes.DictNode(pos, key_value_pairs = []) + # syntax error, try to find a good error message + if len(parts) == 1 and not isinstance(parts[0], list): + s.error("iterable unpacking cannot be used in comprehension") + else: + # e.g. "{1,2,3 for ..." + s.expect('}') + return ExprNodes.DictNode(pos, key_value_pairs=[]) + + s.expect('}') + if target_type == 1: + # (merged) set literal + items = [] + set_items = [] + for part in parts: + if isinstance(part, list): + set_items.extend(part) + else: + if set_items: + items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items)) + set_items = [] + items.append(part) + if set_items: + items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items)) + if len(items) == 1 and items[0].is_set_literal: + return items[0] + return ExprNodes.MergedSequenceNode(pos, args=items, type=Builtin.set_type) + else: + # (merged) dict literal + items = [] + dict_items = [] + for part in parts: + if isinstance(part, list): + dict_items.extend(part) + else: + if dict_items: + items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items)) + dict_items = [] + items.append(part) + if dict_items: + items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items)) + if len(items) == 1 and items[0].is_dict_literal: + return items[0] + return ExprNodes.MergedDictNode(pos, keyword_args=items, reject_duplicates=False) + # NOTE: no longer in Py3 :) def p_backquote_expr(s): @@ -1040,10 +1121,11 @@ def p_simple_expr_list(s, expr=None): s.next() return exprs + def p_test_or_starred_expr_list(s, expr=None): exprs = expr is not None and [expr] or [] while s.sy not in expr_terminators: - exprs.append( p_test_or_starred_expr(s) ) + exprs.append(p_test_or_starred_expr(s)) if s.sy != ',': break s.next() @@ -1295,7 +1377,6 @@ def p_import_statement(s): stats = [] is_absolute = Future.absolute_import in s.context.future_directives for pos, target_name, dotted_name, as_name in items: - dotted_name = EncodedString(dotted_name) if kind == 'cimport': stat = Nodes.CImportStatNode( pos, @@ -1305,7 +1386,7 @@ def p_import_statement(s): else: if as_name and "." in dotted_name: name_list = ExprNodes.ListNode(pos, args=[ - ExprNodes.IdentifierStringNode(pos, value=EncodedString("*"))]) + ExprNodes.IdentifierStringNode(pos, value=s.context.intern_ustring("*"))]) else: name_list = None stat = Nodes.SingleAssignmentNode( @@ -1334,7 +1415,7 @@ def p_from_import_statement(s, first_statement = 0): level = None if level is not None and s.sy in ('import', 'cimport'): # we are dealing with "from .. import foo, bar" - dotted_name_pos, dotted_name = s.position(), '' + dotted_name_pos, dotted_name = s.position(), s.context.intern_ustring('') else: if level is None and Future.absolute_import in s.context.future_directives: level = 0 @@ -1347,7 +1428,7 @@ def p_from_import_statement(s, first_statement = 0): is_cimport = kind == 'cimport' is_parenthesized = False if s.sy == '*': - imported_names = [(s.position(), "*", None, None)] + imported_names = [(s.position(), s.context.intern_ustring("*"), None, None)] s.next() else: if s.sy == '(': @@ -1361,7 +1442,6 @@ def p_from_import_statement(s, first_statement = 0): imported_names.append(p_imported_name(s, is_cimport)) if is_parenthesized: s.expect(')') - dotted_name = EncodedString(dotted_name) if dotted_name == '__future__': if not first_statement: s.error("from __future__ imports must occur at the beginning of the file") @@ -1388,16 +1468,12 @@ def p_from_import_statement(s, first_statement = 0): imported_name_strings = [] items = [] for (name_pos, name, as_name, kind) in imported_names: - encoded_name = EncodedString(name) imported_name_strings.append( - ExprNodes.IdentifierStringNode(name_pos, value = encoded_name)) + ExprNodes.IdentifierStringNode(name_pos, value=name)) items.append( - (name, - ExprNodes.NameNode(name_pos, - name = as_name or name))) + (name, ExprNodes.NameNode(name_pos, name=as_name or name))) import_list = ExprNodes.ListNode( - imported_names[0][0], args = imported_name_strings) - dotted_name = EncodedString(dotted_name) + imported_names[0][0], args=imported_name_strings) return Nodes.FromImportStatNode(pos, module = ExprNodes.ImportNode(dotted_name_pos, module_name = ExprNodes.IdentifierStringNode(pos, value = dotted_name), @@ -1405,8 +1481,8 @@ def p_from_import_statement(s, first_statement = 0): name_list = import_list), items = items) -imported_name_kinds = cython.declare( - set, set(['class', 'struct', 'union'])) + +imported_name_kinds = cython.declare(set, set(['class', 'struct', 'union'])) def p_imported_name(s, is_cimport): pos = s.position() @@ -1418,6 +1494,7 @@ def p_imported_name(s, is_cimport): as_name = p_as_name(s) return (pos, name, as_name, kind) + def p_dotted_name(s, as_allowed): pos = s.position() target_name = p_ident(s) @@ -1428,7 +1505,8 @@ def p_dotted_name(s, as_allowed): names.append(p_ident(s)) if as_allowed: as_name = p_as_name(s) - return (pos, target_name, u'.'.join(names), as_name) + return (pos, target_name, s.context.intern_ustring(u'.'.join(names)), as_name) + def p_as_name(s): if s.sy == 'IDENT' and s.systring == 'as': @@ -1437,6 +1515,7 @@ def p_as_name(s): else: return None + def p_assert_statement(s): # s.sy == 'assert' pos = s.position() @@ -1449,6 +1528,7 @@ def p_assert_statement(s): value = None return Nodes.AssertStatNode(pos, cond = cond, value = value) + statement_terminators = cython.declare(set, set([';', 'NEWLINE', 'EOF'])) def p_if_statement(s): @@ -1993,8 +2073,7 @@ def p_positional_and_keyword_args(s, end_sy_set, templates = None): arg = Nodes.CComplexBaseTypeNode(base_type.pos, base_type = base_type, declarator = declarator) parsed_type = True - keyword_node = ExprNodes.IdentifierStringNode( - arg.pos, value = EncodedString(ident)) + keyword_node = ExprNodes.IdentifierStringNode(arg.pos, value=ident) keyword_args.append((keyword_node, arg)) was_keyword = True @@ -2361,7 +2440,7 @@ def p_c_declarator(s, ctx = Ctx(), empty = 0, is_type = 0, cmethod_flag = 0, if s.sy == '(': s.next() if s.sy == ')' or looking_at_name(s): - base = Nodes.CNameDeclaratorNode(pos, name = EncodedString(u""), cname = None) + base = Nodes.CNameDeclaratorNode(pos, name=s.context.intern_ustring(u""), cname=None) result = p_c_func_declarator(s, pos, ctx, base, cmethod_flag) else: result = p_c_declarator(s, ctx, empty = empty, is_type = is_type, @@ -2454,7 +2533,7 @@ def p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag, else: rhs = None if s.sy == 'IDENT': - name = EncodedString(s.systring) + name = s.systring if empty: error(s.position(), "Declarator should be empty") s.next() @@ -2913,11 +2992,10 @@ def p_decorators(s): s.next() decstring = p_dotted_name(s, as_allowed=0)[2] names = decstring.split('.') - decorator = ExprNodes.NameNode(pos, name=EncodedString(names[0])) + decorator = ExprNodes.NameNode(pos, name=s.context.intern_ustring(names[0])) for name in names[1:]: - decorator = ExprNodes.AttributeNode(pos, - attribute=EncodedString(name), - obj=decorator) + decorator = ExprNodes.AttributeNode( + pos, attribute=s.context.intern_ustring(name), obj=decorator) if s.sy == '(': decorator = p_call(s, decorator) decorators.append(Nodes.DecoratorNode(pos, decorator=decorator)) @@ -2928,7 +3006,7 @@ def p_def_statement(s, decorators=None): # s.sy == 'def' pos = s.position() s.next() - name = EncodedString( p_ident(s) ) + name = p_ident(s) s.expect('(') args, star_arg, starstar_arg = p_varargslist(s, terminator=')') s.expect(')') @@ -2973,20 +3051,18 @@ def p_py_arg_decl(s, annotated = 1): annotation = p_test(s) return Nodes.PyArgDeclNode(pos, name = name, annotation = annotation) + def p_class_statement(s, decorators): # s.sy == 'class' pos = s.position() s.next() - class_name = EncodedString( p_ident(s) ) - class_name.encoding = s.source_encoding + class_name = EncodedString(p_ident(s)) + class_name.encoding = s.source_encoding # FIXME: why is this needed? arg_tuple = None keyword_dict = None - starstar_arg = None if s.sy == '(': - positional_args, keyword_args, star_arg, starstar_arg = \ - p_call_parse_args(s, allow_genexp = False) - arg_tuple, keyword_dict = p_call_build_packed_args( - pos, positional_args, keyword_args, star_arg, None) + positional_args, keyword_args = p_call_parse_args(s, allow_genexp=False) + arg_tuple, keyword_dict = p_call_build_packed_args(pos, positional_args, keyword_args) if arg_tuple is None: # XXX: empty arg_tuple arg_tuple = ExprNodes.TupleNode(pos, args=[]) @@ -2995,10 +3071,10 @@ def p_class_statement(s, decorators): pos, name=class_name, bases=arg_tuple, keyword_args=keyword_dict, - starstar_arg=starstar_arg, doc=doc, body=body, decorators=decorators, force_py3_semantics=s.context.language_level >= 3) + def p_c_class_definition(s, pos, ctx): # s.sy == 'class' s.next() diff --git a/Cython/Compiler/Pipeline.py b/Cython/Compiler/Pipeline.py index 7e2a44ddc7dfd2506385290040f8844dbcfb8d88..305d70595fd8304c873ec25e90cf90ef49534855 100644 --- a/Cython/Compiler/Pipeline.py +++ b/Cython/Compiler/Pipeline.py @@ -30,8 +30,7 @@ def parse_stage_factory(context): full_module_name = compsrc.full_module_name initial_pos = (source_desc, 1, 0) saved_cimport_from_pyx, Options.cimport_from_pyx = Options.cimport_from_pyx, False - scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0, - check_module_name = not Options.embed) + scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0) Options.cimport_from_pyx = saved_cimport_from_pyx tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name) tree.compilation_source = compsrc diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 2543f73d76e85ae91a614a1eaefbf830f091533c..a81b0395d7167a3a372088e6484ffd0a51a60e9b 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -393,7 +393,7 @@ class CTypedefType(BaseType): base_type = self.typedef_base_type if type(base_type) is CIntType: self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name() - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "CIntToPy", "TypeConversion.c", context={"TYPE": self.empty_declaration_code(), "TO_PY_FUNCTION": self.to_py_function})) @@ -415,7 +415,7 @@ class CTypedefType(BaseType): base_type = self.typedef_base_type if type(base_type) is CIntType: self.from_py_function = "__Pyx_PyInt_As_" + self.specialization_name() - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "CIntFromPy", "TypeConversion.c", context={"TYPE": self.empty_declaration_code(), "FROM_PY_FUNCTION": self.from_py_function})) @@ -450,17 +450,17 @@ class CTypedefType(BaseType): type = self.empty_declaration_code() name = self.specialization_name() if binop == "lshift": - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "LeftShift", "Overflow.c", context={'TYPE': type, 'NAME': name, 'SIGNED': self.signed})) else: if const_rhs: binop += "_const" _load_overflow_base(env) - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "SizeCheck", "Overflow.c", context={'TYPE': type, 'NAME': name})) - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "Binop", "Overflow.c", context={'TYPE': type, 'NAME': name, 'BINOP': binop})) return "__Pyx_%s_%s_checking_overflow" % (binop, name) @@ -697,10 +697,9 @@ class MemoryViewSliceType(PyrexType): # We don't have 'code', so use a LazyUtilityCode with a callback. def lazy_utility_callback(code): - context['dtype_typeinfo'] = Buffer.get_type_information_cname( - code, self.dtype) + context['dtype_typeinfo'] = Buffer.get_type_information_cname(code, self.dtype) return TempitaUtilityCode.load( - "ObjectToMemviewSlice", "MemoryView_C.c", context=context) + "ObjectToMemviewSlice", "MemoryView_C.c", context=context) env.use_utility_code(Buffer.acquire_utility_code) env.use_utility_code(MemoryView.memviewslice_init_code) @@ -777,8 +776,8 @@ class MemoryViewSliceType(PyrexType): error_condition = error_condition, ) - utility = TempitaUtilityCode.load( - utility_name, "MemoryView_C.c", context=context) + utility = TempitaUtilityCode.load_cached( + utility_name, "MemoryView_C.c", context=context) env.use_utility_code(utility) return get_function, set_function @@ -1490,7 +1489,7 @@ class CIntType(CNumericType): def create_to_py_utility_code(self, env): if type(self).to_py_function is None: self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name() - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "CIntToPy", "TypeConversion.c", context={"TYPE": self.empty_declaration_code(), "TO_PY_FUNCTION": self.to_py_function})) @@ -1499,7 +1498,7 @@ class CIntType(CNumericType): def create_from_py_utility_code(self, env): if type(self).from_py_function is None: self.from_py_function = "__Pyx_PyInt_As_" + self.specialization_name() - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "CIntFromPy", "TypeConversion.c", context={"TYPE": self.empty_declaration_code(), "FROM_PY_FUNCTION": self.from_py_function})) @@ -1539,18 +1538,18 @@ class CIntType(CNumericType): type = self.empty_declaration_code() name = self.specialization_name() if binop == "lshift": - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "LeftShift", "Overflow.c", context={'TYPE': type, 'NAME': name, 'SIGNED': self.signed})) else: if const_rhs: binop += "_const" if type in ('int', 'long', 'long long'): - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "BaseCaseSigned", "Overflow.c", context={'INT': type, 'NAME': name})) elif type in ('unsigned int', 'unsigned long', 'unsigned long long'): - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "BaseCaseUnsigned", "Overflow.c", context={'UINT': type, 'NAME': name})) elif self.rank <= 1: @@ -1558,22 +1557,23 @@ class CIntType(CNumericType): return "__Pyx_%s_%s_no_overflow" % (binop, name) else: _load_overflow_base(env) - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "SizeCheck", "Overflow.c", context={'TYPE': type, 'NAME': name})) - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "Binop", "Overflow.c", context={'TYPE': type, 'NAME': name, 'BINOP': binop})) return "__Pyx_%s_%s_checking_overflow" % (binop, name) + def _load_overflow_base(env): env.use_utility_code(UtilityCode.load("Common", "Overflow.c")) for type in ('int', 'long', 'long long'): - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "BaseCaseSigned", "Overflow.c", context={'INT': type, 'NAME': type.replace(' ', '_')})) for type in ('unsigned int', 'unsigned long', 'unsigned long long'): - env.use_utility_code(TempitaUtilityCode.load( + env.use_utility_code(TempitaUtilityCode.load_cached( "BaseCaseUnsigned", "Overflow.c", context={'UINT': type, 'NAME': type.replace(' ', '_')})) @@ -2710,8 +2710,9 @@ class CFuncType(CType): trailer = " except %s" % self.exception_value elif self.exception_check == '+': trailer = " except +" - else: - " except *" # ignored + elif self.exception_check and for_display: + # not spelled out by default, unless for human eyes + trailer = " except *" if self.nogil: trailer += " nogil" if not with_calling_convention: @@ -3684,13 +3685,11 @@ class CTupleType(CType): env.use_utility_code(self._convert_from_py_code) return True -c_tuple_types = {} + def c_tuple_type(components): components = tuple(components) - tuple_type = c_tuple_types.get(components) - if tuple_type is None: - cname = Naming.ctuple_type_prefix + type_list_identifier(components) - tuple_type = c_tuple_types[components] = CTupleType(cname, components) + cname = Naming.ctuple_type_prefix + type_list_identifier(components) + tuple_type = CTupleType(cname, components) return tuple_type diff --git a/Cython/Compiler/Scanning.py b/Cython/Compiler/Scanning.py index d01bc19ea4f2b03201cf424ef09053842e300f06..6170cea0126b12de51071369cc0d963e8cd739c2 100644 --- a/Cython/Compiler/Scanning.py +++ b/Cython/Compiler/Scanning.py @@ -6,7 +6,7 @@ from __future__ import absolute_import import cython -cython.declare(EncodedString=object, make_lexicon=object, lexicon=object, +cython.declare(make_lexicon=object, lexicon=object, any_string_prefix=unicode, IDENT=unicode, print_function=object, error=object, warning=object, os=object, platform=object) @@ -21,8 +21,6 @@ from .Errors import error, warning from .Lexicon import any_string_prefix, make_lexicon, IDENT from .Future import print_function -from .StringEncoding import EncodedString - debug_scanner = 0 trace_scanner = 0 scanner_debug_flags = 0 @@ -421,14 +419,11 @@ class PyrexScanner(Scanner): if systring in self.keywords: if systring == u'print' and print_function in self.context.future_directives: self.keywords.discard('print') - systring = EncodedString(systring) elif systring == u'exec' and self.context.language_level >= 3: self.keywords.discard('exec') - systring = EncodedString(systring) else: sy = systring - else: - systring = EncodedString(systring) + systring = self.context.intern_ustring(systring) self.sy = sy self.systring = systring if False: # debug_scanner: diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 1437f832358288e83b33a977701a4e6207196bf8..c2f3ac847923382eb3e72c49c6b73edd61a40531 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -610,8 +610,8 @@ class Scope(object): self.sue_entries.append(entry) return entry - def declare_tuple_type(self, pos, type): - return self.outer_scope.declare_tuple_type(pos, type) + def declare_tuple_type(self, pos, components): + return self.outer_scope.declare_tuple_type(pos, components) def declare_var(self, name, type, pos, cname = None, visibility = 'private', @@ -1056,6 +1056,7 @@ class ModuleScope(Scope): self.cached_builtins = [] self.undeclared_cached_builtins = [] self.namespace_cname = self.module_cname + self._cached_tuple_types = {} for var_name in ['__builtins__', '__name__', '__file__', '__doc__', '__path__']: self.declare_var(EncodedString(var_name), py_object_type, None) @@ -1075,18 +1076,24 @@ class ModuleScope(Scope): return self.outer_scope.lookup(name, language_level=language_level) - def declare_tuple_type(self, pos, type): - cname = type.cname + def declare_tuple_type(self, pos, components): + components = tuple(components) + try: + ttype = self._cached_tuple_types[components] + except KeyError: + ttype = self._cached_tuple_types[components] = PyrexTypes.c_tuple_type(components) + cname = ttype.cname entry = self.lookup_here(cname) if not entry: scope = StructOrUnionScope(cname) - for ix, component in enumerate(type.components): + for ix, component in enumerate(components): scope.declare_var(name="f%s" % ix, type=component, pos=pos) - struct_entry = self.declare_struct_or_union(cname + '_struct', 'struct', scope, typedef_flag=True, pos=pos, cname=cname) + struct_entry = self.declare_struct_or_union( + cname + '_struct', 'struct', scope, typedef_flag=True, pos=pos, cname=cname) self.type_entries.remove(struct_entry) - type.struct_entry = struct_entry - entry = self.declare_type(cname, type, pos, cname) - type.entry = entry + ttype.struct_entry = struct_entry + entry = self.declare_type(cname, ttype, pos, cname) + ttype.entry = entry return entry def declare_builtin(self, name, pos): @@ -1127,14 +1134,23 @@ class ModuleScope(Scope): # relative imports relative to this module's parent. # Finds and parses the module's .pxd file if the module # has not been referenced before. - module_scope = self.global_scope() + relative_to = None + absolute_fallback = False if relative_level is not None and relative_level > 0: - # merge current absolute module name and relative import name into qualified name - current_module = module_scope.qualified_name.split('.') - base_package = current_module[:-relative_level] - module_name = '.'.join(base_package + (module_name.split('.') if module_name else [])) + # explicit relative cimport + # error of going beyond top-level is handled in cimport node + relative_to = self + while relative_level > 0 and relative_to: + relative_to = relative_to.parent_module + relative_level -= 1 + elif relative_level != 0: + # -1 or None: try relative cimport first, then absolute + relative_to = self.parent_module + absolute_fallback = True + + module_scope = self.global_scope() return module_scope.context.find_module( - module_name, relative_to=None if relative_level == 0 else self.parent_module, pos=pos) + module_name, relative_to=relative_to, pos=pos, absolute_fallback=absolute_fallback) def find_submodule(self, name): # Find and return scope for a submodule of this module, @@ -1258,6 +1274,8 @@ class ModuleScope(Scope): cname = name else: cname = self.mangle(Naming.func_prefix, name) + if visibility == 'extern' and type.optional_arg_count: + error(pos, "Extern functions cannot have default arguments values.") entry = self.lookup_here(name) if entry and entry.defined_in_pxd: if entry.visibility != "private": diff --git a/Cython/Compiler/TreeFragment.py b/Cython/Compiler/TreeFragment.py index 352699533b7577a0b197eb5b337e38b4117ed24f..e08acb2895b836b40401cc69b0882d215204db39 100644 --- a/Cython/Compiler/TreeFragment.py +++ b/Cython/Compiler/TreeFragment.py @@ -32,7 +32,7 @@ class StringParseContext(Main.Context): create_testscope=False) self.module_name = name - def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1): + def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, absolute_fallback=True): if module_name not in (self.module_name, 'cython'): raise AssertionError("Not yet supporting any cimports/includes from string code snippets") return ModuleScope(module_name, parent_module=None, context=self) diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py index 66b504c3c0470cbe2a4bfbdd27fc132cacd63553..94d00915811fd3d0bcc6b96bc6fdbb554c3c4bbb 100644 --- a/Cython/Compiler/TypeInference.py +++ b/Cython/Compiler/TypeInference.py @@ -12,8 +12,10 @@ from .Visitor import CythonTransform, EnvTransform class TypedExprNode(ExprNodes.ExprNode): # Used for declaring assignments of a specified type without a known entry. - def __init__(self, type): - self.type = type + subexprs = [] + + def __init__(self, type, pos=None): + super(TypedExprNode, self).__init__(pos, type=type) object_expr = TypedExprNode(py_object_type) @@ -184,10 +186,10 @@ class MarkParallelAssignments(EnvTransform): # use fake expressions with the right result type if node.star_arg: self.mark_assignment( - node.star_arg, TypedExprNode(Builtin.tuple_type)) + node.star_arg, TypedExprNode(Builtin.tuple_type, node.pos)) if node.starstar_arg: self.mark_assignment( - node.starstar_arg, TypedExprNode(Builtin.dict_type)) + node.starstar_arg, TypedExprNode(Builtin.dict_type, node.pos)) EnvTransform.visit_FuncDefNode(self, node) return node diff --git a/Cython/Compiler/UtilityCode.py b/Cython/Compiler/UtilityCode.py index 01c2c9e81de9e019af71aaf34aaaa1ca2705d442..9f7bebb814d07324fa15a9d16af26808e8053061 100644 --- a/Cython/Compiler/UtilityCode.py +++ b/Cython/Compiler/UtilityCode.py @@ -34,7 +34,9 @@ class NonManglingModuleScope(Symtab.ModuleScope): class CythonUtilityCodeContext(StringParseContext): scope = None - def find_module(self, module_name, relative_to=None, pos=None, need_pxd=True): + def find_module(self, module_name, relative_to=None, pos=None, need_pxd=True, absolute_fallback=True): + if relative_to: + raise AssertionError("Relative imports not supported in utility code.") if module_name != self.module_name: if module_name not in self.modules: raise AssertionError("Only the cython cimport is supported.") diff --git a/Cython/Compiler/Visitor.py b/Cython/Compiler/Visitor.py index 7222edef34d2f7fdbff3fe2dfbefcc93c5d6d381..6c7b7b2f21c3f1bcbba09e41ce11e3689f6e5102 100644 --- a/Cython/Compiler/Visitor.py +++ b/Cython/Compiler/Visitor.py @@ -14,6 +14,7 @@ from . import Nodes from . import ExprNodes from . import Errors from . import DebugFlags +from . import Future import cython @@ -434,7 +435,7 @@ find_special_method_for_binary_operator = { '>': '__gt__', '+': '__add__', '&': '__and__', - '/': '__truediv__', + '/': '__div__', '//': '__floordiv__', '<<': '__lshift__', '%': '__mod__', @@ -515,6 +516,9 @@ class MethodDispatcherTransform(EnvTransform): operand1, operand2 = node.operand1, node.operand2 if special_method_name == '__contains__': operand1, operand2 = operand2, operand1 + elif special_method_name == '__div__': + if Future.division in self.current_env().global_scope().context.future_directives: + special_method_name = '__truediv__' obj_type = operand1.type if obj_type.is_builtin_type: type_name = obj_type.name @@ -705,12 +709,17 @@ class PrintTree(TreeVisitor): """Prints a representation of the tree to standard output. Subclass and override repr_of to provide more information about nodes. """ - def __init__(self): + def __init__(self, start=None, end=None): TreeVisitor.__init__(self) self._indent = "" + if start is not None or end is not None: + self._line_range = (start or 0, end or 2**30) + else: + self._line_range = None def indent(self): self._indent += " " + def unindent(self): self._indent = self._indent[:-2] @@ -724,15 +733,17 @@ class PrintTree(TreeVisitor): # under the parent-node, not displaying the list itself in # the hierarchy. def visit_Node(self, node): - if len(self.access_path) == 0: - name = "(root)" - else: - parent, attr, idx = self.access_path[-1] - if idx is not None: - name = "%s[%d]" % (attr, idx) + line = node.pos[1] + if self._line_range is None or self._line_range[0] <= line <= self._line_range[1]: + if len(self.access_path) == 0: + name = "(root)" else: - name = attr - print("%s- %s: %s" % (self._indent, name, self.repr_of(node))) + parent, attr, idx = self.access_path[-1] + if idx is not None: + name = "%s[%d]" % (attr, idx) + else: + name = attr + print("%s- %s: %s" % (self._indent, name, self.repr_of(node))) self.indent() self.visitchildren(node) self.unindent() diff --git a/Cython/Includes/cpython/bytes.pxd b/Cython/Includes/cpython/bytes.pxd index 2fb350201539f5761b4276003c443daa741a7f28..ea72c6aae7757c521cc5fcda5d55df9c89229ac3 100644 --- a/Cython/Includes/cpython/bytes.pxd +++ b/Cython/Includes/cpython/bytes.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": ctypedef struct va_list diff --git a/Cython/Includes/cpython/cobject.pxd b/Cython/Includes/cpython/cobject.pxd index 62c47064f5493ac60e2cbeb6a5a12a777e777b19..497d8a92e803599715bd4f59b95aaafb2e9c0671 100644 --- a/Cython/Includes/cpython/cobject.pxd +++ b/Cython/Includes/cpython/cobject.pxd @@ -1,4 +1,3 @@ -from cpython.ref cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/datetime.pxd b/Cython/Includes/cpython/datetime.pxd index 3c6f8a7f3969251e83ba3f303b84e4c124eae008..2e0c4bd88b9aaf3ed98049d6340acddb2652eb02 100644 --- a/Cython/Includes/cpython/datetime.pxd +++ b/Cython/Includes/cpython/datetime.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from cpython.object cimport PyObject cdef extern from "Python.h": ctypedef struct PyTypeObject: diff --git a/Cython/Includes/cpython/dict.pxd b/Cython/Includes/cpython/dict.pxd index 69a416aeefd4f9dac5ee188364ecf1ebad3504a3..d58faea9a802a90e97c16a74d4d9858a3f6e7260 100644 --- a/Cython/Includes/cpython/dict.pxd +++ b/Cython/Includes/cpython/dict.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/exc.pxd b/Cython/Includes/cpython/exc.pxd index 89beb214a4127b08cf3e457db4914d431a3f7eb0..2e4eda5a03c16b1a07d730b0015f0bed6537c789 100644 --- a/Cython/Includes/cpython/exc.pxd +++ b/Cython/Includes/cpython/exc.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/function.pxd b/Cython/Includes/cpython/function.pxd index e8e4f068cfa68b485dbe4f38adcef5cc84579aa0..0002a3f6cbc426dc05dbdfce454da44483fbfa3e 100644 --- a/Cython/Includes/cpython/function.pxd +++ b/Cython/Includes/cpython/function.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/getargs.pxd b/Cython/Includes/cpython/getargs.pxd index 591aefbfdeda65dc902c679d6d3a19a1c0c1f0ed..be6df3285a720af1cdd21e5f40267686f9042526 100644 --- a/Cython/Includes/cpython/getargs.pxd +++ b/Cython/Includes/cpython/getargs.pxd @@ -1,4 +1,3 @@ -from cpython.ref cimport PyObject cdef extern from "Python.h": ##################################################################### diff --git a/Cython/Includes/cpython/list.pxd b/Cython/Includes/cpython/list.pxd index 5cfd5735f6a71b2ee54e2e2f8394251ddfd93823..c6a29535c9aafb890c437477a224acc9b167cd99 100644 --- a/Cython/Includes/cpython/list.pxd +++ b/Cython/Includes/cpython/list.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/method.pxd b/Cython/Includes/cpython/method.pxd index bc09416422b6a0194828390de339e8c1d311229b..f51ebcc7c75b41477211925f13e87a1bea7dacb3 100644 --- a/Cython/Includes/cpython/method.pxd +++ b/Cython/Includes/cpython/method.pxd @@ -1,5 +1,6 @@ +from .object cimport PyObject + cdef extern from "Python.h": - ctypedef void PyObject ############################################################################ # 7.5.4 Method Objects ############################################################################ diff --git a/Cython/Includes/cpython/module.pxd b/Cython/Includes/cpython/module.pxd index c821896058943c6478d8bcfd2ccefda79fef34f4..f36b9892ba3816700a9ba814d1f6703f74c46dca 100644 --- a/Cython/Includes/cpython/module.pxd +++ b/Cython/Includes/cpython/module.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": ctypedef struct _inittab diff --git a/Cython/Includes/cpython/number.pxd b/Cython/Includes/cpython/number.pxd index 346546e3299d7871c820bfddd4044dd9ebe188aa..3ad8de59c07cee4c7aa04310694306771e2f56e2 100644 --- a/Cython/Includes/cpython/number.pxd +++ b/Cython/Includes/cpython/number.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/object.pxd b/Cython/Includes/cpython/object.pxd index d861965788e8a45bb911ac0fcc67d2762b9d7035..ebd1acd2c97b7b99b4fdf9f7268094283396781a 100644 --- a/Cython/Includes/cpython/object.pxd +++ b/Cython/Includes/cpython/object.pxd @@ -1,8 +1,69 @@ -from cpython.ref cimport PyObject, PyTypeObject from libc.stdio cimport FILE +cimport cpython.type cdef extern from "Python.h": + ctypedef struct PyObject # forward declaration + + ctypedef object (*newfunc)(cpython.type.type, object, object) # (type, args, kwargs) + + ctypedef object (*unaryfunc)(object) + ctypedef object (*binaryfunc)(object, object) + ctypedef object (*ternaryfunc)(object, object, object) + ctypedef int (*inquiry)(object) + ctypedef Py_ssize_t (*lenfunc)(object) + ctypedef object (*ssizeargfunc)(object, Py_ssize_t) + ctypedef object (*ssizessizeargfunc)(object, Py_ssize_t, Py_ssize_t) + ctypedef int (*ssizeobjargproc)(object, Py_ssize_t, object) + ctypedef int (*ssizessizeobjargproc)(object, Py_ssize_t, Py_ssize_t, object) + ctypedef int (*objobjargproc)(object, object, object) + ctypedef int (*objobjproc)(object, object) + + ctypedef Py_hash_t (*hashfunc)(object) + ctypedef object (*reprfunc)(object) + + ctypedef int (*cmpfunc)(object, object) + ctypedef object (*richcmpfunc)(object, object, int) + + # The following functions use 'PyObject*' as first argument instead of 'object' to prevent + # accidental reference counting when calling them during a garbage collection run. + ctypedef void (*destructor)(PyObject*) + ctypedef int (*visitproc)(PyObject*, void *) + ctypedef int (*traverseproc)(PyObject*, visitproc, void*) + + ctypedef struct PyTypeObject: + const char* tp_name + const char* tp_doc + Py_ssize_t tp_basicsize + Py_ssize_t tp_itemsize + Py_ssize_t tp_dictoffset + unsigned long tp_flags + + newfunc tp_new + destructor tp_dealloc + traverseproc tp_traverse + inquiry tp_clear + + ternaryfunc tp_call + hashfunc tp_hash + reprfunc tp_str + reprfunc tp_repr + + cmpfunc tp_compare + richcmpfunc tp_richcompare + + PyTypeObject* tp_base + + ctypedef struct PyObject: + Py_ssize_t ob_refcnt + PyTypeObject *ob_type + + cdef PyTypeObject *Py_TYPE(object) + + void* PyObject_Malloc(size_t) + void* PyObject_Realloc(void *, size_t) + void PyObject_Free(void *) + ##################################################################### # 6.1 Object Protocol ##################################################################### diff --git a/Cython/Includes/cpython/pycapsule.pxd b/Cython/Includes/cpython/pycapsule.pxd index f0b326bc2cba32402c63a17f5a926a3de70f893d..449f369a2cd0b712ffb58ed6554faf5d4acf8537 100644 --- a/Cython/Includes/cpython/pycapsule.pxd +++ b/Cython/Includes/cpython/pycapsule.pxd @@ -1,4 +1,3 @@ -from cpython.ref cimport PyObject # available since Python 3.1! diff --git a/Cython/Includes/cpython/pystate.pxd b/Cython/Includes/cpython/pystate.pxd index d53259f3779fb98905b5c25af5c35bfd83c0b8ab..f58503f9e6237e8b5b0df3a364326664330704f2 100644 --- a/Cython/Includes/cpython/pystate.pxd +++ b/Cython/Includes/cpython/pystate.pxd @@ -1,6 +1,6 @@ # Thread and interpreter state structures and their interfaces -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/ref.pxd b/Cython/Includes/cpython/ref.pxd index 63ee54b8415e8b6aa16918261c3d2763fa5c8758..4bc9a7d7c887986d1ce4851f3ac0ad73ccbe7e2b 100644 --- a/Cython/Includes/cpython/ref.pxd +++ b/Cython/Includes/cpython/ref.pxd @@ -1,15 +1,6 @@ -cdef extern from "Python.h": - ctypedef struct PyTypeObject: - Py_ssize_t tp_basicsize - Py_ssize_t tp_itemsize - long tp_flags - - ctypedef struct PyObject: - Py_ssize_t ob_refcnt - PyTypeObject *ob_type - cdef PyTypeObject *Py_TYPE(object) - +from .object cimport PyObject, PyTypeObject, Py_TYPE # legacy imports for re-export +cdef extern from "Python.h": ##################################################################### # 3. Reference Counts ##################################################################### diff --git a/Cython/Includes/cpython/sequence.pxd b/Cython/Includes/cpython/sequence.pxd index 61ddca2f7b4b97c58ac95fe96b871014393a31c8..eb279968d20a3654cffdac8e570ec56df3a354ed 100644 --- a/Cython/Includes/cpython/sequence.pxd +++ b/Cython/Includes/cpython/sequence.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/string.pxd b/Cython/Includes/cpython/string.pxd index 65c6d371c73edba59f676ff2c0764ecda0b352c6..8af78f3dde615e2b2892a1e36b9f94754a9f2fa7 100644 --- a/Cython/Includes/cpython/string.pxd +++ b/Cython/Includes/cpython/string.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": ctypedef struct va_list diff --git a/Cython/Includes/cpython/tuple.pxd b/Cython/Includes/cpython/tuple.pxd index 6da28902cd68f0ebd64804e69c5c97c191ea7894..6564b5013926a0f38cf15216acb63b5fa6d0cbfe 100644 --- a/Cython/Includes/cpython/tuple.pxd +++ b/Cython/Includes/cpython/tuple.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/cpython/weakref.pxd b/Cython/Includes/cpython/weakref.pxd index 8f510526afde9e288913eca0d5c296024181dc54..ae710be6975252441425e0c49be4445fc1bbe907 100644 --- a/Cython/Includes/cpython/weakref.pxd +++ b/Cython/Includes/cpython/weakref.pxd @@ -1,4 +1,4 @@ -from cpython.ref cimport PyObject +from .object cimport PyObject cdef extern from "Python.h": diff --git a/Cython/Includes/libcpp/complex.pxd b/Cython/Includes/libcpp/complex.pxd index 4645c9e2b22c33e39783252c8248984a33c5305e..c875d5e5bd9628e5cb6519811fc34dae30de788a 100644 --- a/Cython/Includes/libcpp/complex.pxd +++ b/Cython/Includes/libcpp/complex.pxd @@ -39,63 +39,63 @@ cdef extern from "<complex>" namespace "std" nogil: void imag(T) # Return real part - T real(complex[T]&) + T real[T](complex[T]&) long double real(long double) double real(double) float real(float) # Return imaginary part - T imag(complex[T]&) + T imag[T](complex[T]&) long double imag(long double) double imag(double) float imag(float) - T abs(complex[T]&) - T arg(complex[T]&) + T abs[T](complex[T]&) + T arg[T](complex[T]&) long double arg(long double) double arg(double) float arg(float) - T norm(complex[T]) + T norm[T](complex[T]) long double norm(long double) double norm(double) float norm(float) - complex[T] conj(complex[T]&) + complex[T] conj[T](complex[T]&) complex[long double] conj(long double) complex[double] conj(double) complex[float] conj(float) - complex[T] proj(complex[T]) + complex[T] proj[T](complex[T]) complex[long double] proj(long double) complex[double] proj(double) complex[float] proj(float) - complex[T] polar(T&, T&) - complex[T] ploar(T&) + complex[T] polar[T](T&, T&) + complex[T] ploar[T](T&) - complex[T] exp(complex[T]&) - complex[T] log(complex[T]&) - complex[T] log10(complex[T]&) + complex[T] exp[T](complex[T]&) + complex[T] log[T](complex[T]&) + complex[T] log10[T](complex[T]&) - complex[T] pow(complex[T]&, complex[T]&) - complex[T] pow(complex[T]&, T&) - complex[T] pow(T&, complex[T]&) + complex[T] pow[T](complex[T]&, complex[T]&) + complex[T] pow[T](complex[T]&, T&) + complex[T] pow[T](T&, complex[T]&) # There are some promotion versions too - complex[T] sqrt(complex[T]&) + complex[T] sqrt[T](complex[T]&) - complex[T] sin(complex[T]&) - complex[T] cos(complex[T]&) - complex[T] tan(complex[T]&) - complex[T] asin(complex[T]&) - complex[T] acos(complex[T]&) - complex[T] atan(complex[T]&) + complex[T] sin[T](complex[T]&) + complex[T] cos[T](complex[T]&) + complex[T] tan[T](complex[T]&) + complex[T] asin[T](complex[T]&) + complex[T] acos[T](complex[T]&) + complex[T] atan[T](complex[T]&) - complex[T] sinh(complex[T]&) - complex[T] cosh(complex[T]&) - complex[T] tanh(complex[T]&) + complex[T] sinh[T](complex[T]&) + complex[T] cosh[T](complex[T]&) + complex[T] tanh[T](complex[T]&) - complex[T] asinh(complex[T]&) - complex[T] acosh(complex[T]&) - complex[T] atanh(complex[T]&) + complex[T] asinh[T](complex[T]&) + complex[T] acosh[T](complex[T]&) + complex[T] atanh[T](complex[T]&) diff --git a/Cython/Includes/libcpp/deque.pxd b/Cython/Includes/libcpp/deque.pxd index 2c34807a82e70a705956e9b4c939e031c3179f4a..d7e7747454db2155520910998bbf5a0ad900fa09 100644 --- a/Cython/Includes/libcpp/deque.pxd +++ b/Cython/Includes/libcpp/deque.pxd @@ -12,8 +12,8 @@ cdef extern from "<deque>" namespace "std" nogil: iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass + cppclass const_iterator(iterator): + pass #cppclass const_reverse_iterator(reverse_iterator): # pass deque() except + @@ -34,11 +34,11 @@ cdef extern from "<deque>" namespace "std" nogil: T& at(size_t) T& back() iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() void clear() bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() iterator erase(iterator) iterator erase(iterator, iterator) T& front() diff --git a/Cython/Includes/libcpp/list.pxd b/Cython/Includes/libcpp/list.pxd index 3e7259c0b9844d4a99219973582705d0f8bd946f..bad97f2bce2527c04afeff0ca59c4d40703a3ed4 100644 --- a/Cython/Includes/libcpp/list.pxd +++ b/Cython/Includes/libcpp/list.pxd @@ -16,10 +16,10 @@ cdef extern from "<list>" namespace "std" nogil: reverse_iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass list() except + list(list&) except + list(size_t, T&) except + @@ -33,11 +33,11 @@ cdef extern from "<list>" namespace "std" nogil: void assign(size_t, T&) T& back() iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() void clear() bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() iterator erase(iterator) iterator erase(iterator, iterator) T& front() @@ -51,11 +51,11 @@ cdef extern from "<list>" namespace "std" nogil: void push_back(T&) void push_front(T&) reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() void remove(T&) #void remove_if(UnPred) reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() void resize(size_t, T&) void reverse() size_t size() diff --git a/Cython/Includes/libcpp/map.pxd b/Cython/Includes/libcpp/map.pxd index a3768d8a2832aa2fdaf017595f3257d5904b8ce9..a1c84f1f6196bba830a492ed080039f8c37536d5 100644 --- a/Cython/Includes/libcpp/map.pxd +++ b/Cython/Includes/libcpp/map.pxd @@ -20,8 +20,8 @@ cdef extern from "<map>" namespace "std" nogil: iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_reverse_iterator(reverse_iterator): + pass map() except + map(map&) except + #map(key_compare&) @@ -53,14 +53,14 @@ cdef extern from "<map>" namespace "std" nogil: #void insert(input_iterator, input_iterator) #key_compare key_comp() iterator lower_bound(const T&) - #const_iterator lower_bound(const key_type&) + const_iterator const_lower_bound "lower_bound"(const T&) size_t max_size() reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() size_t size() void swap(map&) iterator upper_bound(const T&) - #const_iterator upper_bound(const key_type&) + const_iterator const_upper_bound "upper_bound"(const T&) #value_compare value_comp() diff --git a/Cython/Includes/libcpp/set.pxd b/Cython/Includes/libcpp/set.pxd index dd74377134d650f13ffd68121a2f3d358e7866f0..1d9719b841c95201f702b769d3e7c20556db9083 100644 --- a/Cython/Includes/libcpp/set.pxd +++ b/Cython/Includes/libcpp/set.pxd @@ -14,10 +14,10 @@ cdef extern from "<set>" namespace "std" nogil: iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass set() except + set(set&) except + #set(key_compare&) @@ -29,32 +29,32 @@ cdef extern from "<set>" namespace "std" nogil: bint operator<=(set&, set&) bint operator>=(set&, set&) iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() void clear() size_t count(const T&) bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() pair[iterator, iterator] equal_range(const T&) #pair[const_iterator, const_iterator] equal_range(T&) void erase(iterator) void erase(iterator, iterator) size_t erase(T&) iterator find(T&) - #const_iterator find(T&) + const_iterator const_find "find"(T&) pair[iterator, bint] insert(const T&) except + iterator insert(iterator, const T&) except + #void insert(input_iterator, input_iterator) #key_compare key_comp() iterator lower_bound(T&) - #const_iterator lower_bound(T&) + const_iterator const_lower_bound "lower_bound"(T&) size_t max_size() reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() size_t size() void swap(set&) iterator upper_bound(const T&) - #const_iterator upper_bound(const T&) + const_iterator const_upper_bound "upper_bound"(const T&) #value_compare value_comp() diff --git a/Cython/Includes/libcpp/unordered_map.pxd b/Cython/Includes/libcpp/unordered_map.pxd index 3bf4f5deecfc4a96e6ca533a3cf95a06a0d15b14..114b28e25d08f464412c1f9bf61f7f230f17034a 100644 --- a/Cython/Includes/libcpp/unordered_map.pxd +++ b/Cython/Includes/libcpp/unordered_map.pxd @@ -14,10 +14,10 @@ cdef extern from "<unordered_map>" namespace "std" nogil: iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass unordered_map() except + unordered_map(unordered_map&) except + #unordered_map(key_compare&) @@ -31,34 +31,34 @@ cdef extern from "<unordered_map>" namespace "std" nogil: bint operator>=(unordered_map&, unordered_map&) U& at(T&) iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() void clear() size_t count(T&) bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() pair[iterator, iterator] equal_range(T&) #pair[const_iterator, const_iterator] equal_range(key_type&) void erase(iterator) void erase(iterator, iterator) size_t erase(T&) iterator find(T&) - #const_iterator find(key_type&) + const_iterator const_find "find"(T&) pair[iterator, bint] insert(pair[T, U]) # XXX pair[T,U]& iterator insert(iterator, pair[T, U]) # XXX pair[T,U]& #void insert(input_iterator, input_iterator) #key_compare key_comp() iterator lower_bound(T&) - #const_iterator lower_bound(key_type&) + const_iterator const_lower_bound "lower_bound"(T&) size_t max_size() reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() size_t size() void swap(unordered_map&) iterator upper_bound(T&) - #const_iterator upper_bound(key_type&) + const_iterator const_upper_bound "upper_bound"(T&) #value_compare value_comp() void max_load_factor(float) float max_load_factor() diff --git a/Cython/Includes/libcpp/unordered_set.pxd b/Cython/Includes/libcpp/unordered_set.pxd index 55107dbefccae392392befa8f75939c837ceec5a..866902c2e93c0ad08b19e0288c2563e27426e75d 100644 --- a/Cython/Includes/libcpp/unordered_set.pxd +++ b/Cython/Includes/libcpp/unordered_set.pxd @@ -14,10 +14,10 @@ cdef extern from "<unordered_set>" namespace "std" nogil: iterator operator--() bint operator==(reverse_iterator) bint operator!=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass unordered_set() except + unordered_set(unordered_set&) except + #unordered_set(key_compare&) @@ -29,32 +29,32 @@ cdef extern from "<unordered_set>" namespace "std" nogil: bint operator<=(unordered_set&, unordered_set&) bint operator>=(unordered_set&, unordered_set&) iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() void clear() size_t count(T&) bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() pair[iterator, iterator] equal_range(T&) #pair[const_iterator, const_iterator] equal_range(T&) void erase(iterator) void erase(iterator, iterator) size_t erase(T&) iterator find(T&) - #const_iterator find(T&) + const_iterator const_find "find"(T&) pair[iterator, bint] insert(T&) iterator insert(iterator, T&) #void insert(input_iterator, input_iterator) #key_compare key_comp() iterator lower_bound(T&) - #const_iterator lower_bound(T&) + const_iterator const_lower_bound "lower_bound"(T&) size_t max_size() reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() size_t size() void swap(unordered_set&) iterator upper_bound(T&) - #const_iterator upper_bound(T&) + const_iterator const_upper_bound "upper_bound"(T&) #value_compare value_comp() diff --git a/Cython/Includes/libcpp/vector.pxd b/Cython/Includes/libcpp/vector.pxd index 22db3a3370b57d806a14aa52152434de9a4ae266..e8afcd9677678bdece13c775bad79cdedf4854c4 100644 --- a/Cython/Includes/libcpp/vector.pxd +++ b/Cython/Includes/libcpp/vector.pxd @@ -24,10 +24,10 @@ cdef extern from "<vector>" namespace "std" nogil: bint operator>(reverse_iterator) bint operator<=(reverse_iterator) bint operator>=(reverse_iterator) - #cppclass const_iterator(iterator): - # pass - #cppclass const_reverse_iterator(reverse_iterator): - # pass + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass vector() except + vector(vector&) except + vector(size_t) except + @@ -46,12 +46,12 @@ cdef extern from "<vector>" namespace "std" nogil: T& at(size_t) except + T& back() iterator begin() - #const_iterator begin() + const_iterator const_begin "begin"() size_t capacity() void clear() bint empty() iterator end() - #const_iterator end() + const_iterator const_end "end"() iterator erase(iterator) iterator erase(iterator, iterator) T& front() @@ -62,15 +62,15 @@ cdef extern from "<vector>" namespace "std" nogil: void pop_back() void push_back(T&) except + reverse_iterator rbegin() - #const_reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() reverse_iterator rend() - #const_reverse_iterator rend() + const_reverse_iterator const_rend "rend"() void reserve(size_t) void resize(size_t) except + void resize(size_t, T&) except + size_t size() void swap(vector&) - + # C++11 methods T* data() void shrink_to_fit() diff --git a/Cython/Includes/numpy/__init__.pxd b/Cython/Includes/numpy/__init__.pxd index edb1dbfa518e3c44dd4c61c61b64ae7ea43c214e..0ad89f7bca7768ddd153a4cbb574c7db58ac9fe2 100644 --- a/Cython/Includes/numpy/__init__.pxd +++ b/Cython/Includes/numpy/__init__.pxd @@ -241,7 +241,6 @@ cdef extern from "numpy/arrayobject.h": cdef int t cdef char* f = NULL cdef dtype descr = self.descr - cdef list stack cdef int offset cdef bint hasfields = PyDataType_HASFIELDS(descr) @@ -788,8 +787,6 @@ cdef inline char* _util_dtypestring(dtype descr, char* f, char* end, int* offset # string. The new location in the format string is returned. cdef dtype child - cdef int delta_offset - cdef tuple i cdef int endian_detector = 1 cdef bint little_endian = ((<char*>&endian_detector)[0] != 0) cdef tuple fields diff --git a/Cython/Parser/Grammar b/Cython/Parser/Grammar index bea5cfe9127be8b1abe7fc0a421dbf4591c77c76..cb66a36b3c9b626dc0b7978ce6c9e5903a0ab561 100644 --- a/Cython/Parser/Grammar +++ b/Cython/Parser/Grammar @@ -109,17 +109,29 @@ subscript: test | [test] ':' [test] [sliceop] sliceop: ':' [test] exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] testlist: test (',' test)* [','] -dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | - (test (comp_for | (',' test)* [','])) ) +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* [','])) | + ((test | star_expr) + (comp_for | (',' (test | star_expr))* [','])) ) classdef: 'class' PY_NAME ['(' [arglist] ')'] ':' suite -arglist: (argument ',')* (argument [','] - |'*' test (',' argument)* [',' '**' test] - |'**' test) +arglist: argument (',' argument)* [','] + # The reason that keywords are test nodes instead of NAME is that using NAME # results in an ambiguity. ast.c makes sure it's a NAME. -argument: test [comp_for] | test '=' test # Really [keyword '='] test +# "test '=' test" is really "keyword '=' test", but we have no such token. +# These need to be in a single rule to avoid grammar that is ambiguous +# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +# we explicitly match '*' here, too, to give it proper precedence. +# Illegal combinations and orderings are blocked in ast.c: +# multiple (test comp_for) arguements are blocked; keyword unpackings +# that precede iterable unpackings are blocked; etc. +argument: ( test [comp_for] | + test '=' test | + '**' expr | + star_expr ) + comp_iter: comp_for | comp_if comp_for: 'for' exprlist ('in' or_test | for_from_clause) [comp_iter] comp_if: 'if' test_nocond [comp_iter] diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 72b4c0343429296ed2af44e9ce4f0e87b5a974f9..d058a3aa3c15ef90351ad025455a74da8f247b9f 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -1,5 +1,5 @@ # cython.* namespace for pure mode. -__version__ = "0.22" +__version__ = "0.23dev" # BEGIN shameless copy from Cython/minivect/minitypes.py @@ -97,7 +97,7 @@ class _EmptyDecoratorAndManager(object): cclass = ccall = cfunc = _EmptyDecoratorAndManager() -returns = lambda type_arg: _EmptyDecoratorAndManager() +returns = wraparound = boundscheck = lambda arg: _EmptyDecoratorAndManager() final = internal = type_version_tag = no_gc_clear = _empty_decorator diff --git a/Cython/Utility/Buffer.c b/Cython/Utility/Buffer.c index ad8ce0daac977497dc667fa19d6f69ac24288d11..c5865b15e429fe41daaf0b82035e157c344093e1 100644 --- a/Cython/Utility/Buffer.c +++ b/Cython/Utility/Buffer.c @@ -858,16 +858,13 @@ static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type) case 'I': case 'U': if (size == 1) - *buf = 'b'; + *buf = (type->is_unsigned) ? 'B' : 'b'; else if (size == 2) - *buf = 'h'; + *buf = (type->is_unsigned) ? 'H' : 'h'; else if (size == 4) - *buf = 'i'; + *buf = (type->is_unsigned) ? 'I' : 'i'; else if (size == 8) - *buf = 'q'; - - if (type->is_unsigned) - *buf = toupper(*buf); + *buf = (type->is_unsigned) ? 'Q' : 'q'; break; case 'P': *buf = 'P'; diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index 4482a7aab9a9c093160a04f0159d310204063e0a..c9d2a65f620d3149143e7a5f86abbe63faf409fd 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -29,7 +29,7 @@ static PyObject* __Pyx_Globals(void) { goto bad; for (i = PyList_GET_SIZE(names)-1; i >= 0; i--) { #if CYTHON_COMPILING_IN_PYPY - PyObject* name = PySequence_GetItem(names, i); + PyObject* name = PySequence_ITEM(names, i); if (!name) goto bad; #else @@ -239,20 +239,64 @@ static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_abs_longlong(PY_LONG_LONG x) { #endif } + //////////////////// pow2.proto //////////////////// #define __Pyx_PyNumber_Power2(a, b) PyNumber_Power(a, b, Py_None) + +//////////////////// object_ord.proto //////////////////// +//@requires: TypeConversion.c::UnicodeAsUCS4 + +#if PY_MAJOR_VERSION >= 3 +#define __Pyx_PyObject_Ord(c) \ + (likely(PyUnicode_Check(c)) ? (long)__Pyx_PyUnicode_AsPy_UCS4(c) : __Pyx__PyObject_Ord(c)) +#else +#define __Pyx_PyObject_Ord(c) __Pyx__PyObject_Ord(c) +#endif +static long __Pyx__PyObject_Ord(PyObject* c); /*proto*/ + +//////////////////// object_ord //////////////////// + +static long __Pyx__PyObject_Ord(PyObject* c) { + Py_ssize_t size; + if (PyBytes_Check(c)) { + size = PyBytes_GET_SIZE(c); + if (likely(size == 1)) { + return (unsigned char) PyBytes_AS_STRING(c)[0]; + } +#if PY_MAJOR_VERSION < 3 + } else if (PyUnicode_Check(c)) { + return (long)__Pyx_PyUnicode_AsPy_UCS4(c); +#endif +#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) + } else if (PyByteArray_Check(c)) { + size = PyByteArray_GET_SIZE(c); + if (likely(size == 1)) { + return (unsigned char) PyByteArray_AS_STRING(c)[0]; + } +#endif + } else { + // FIXME: support character buffers - but CPython doesn't support them either + PyErr_Format(PyExc_TypeError, + "ord() expected string of length 1, but %.200s found", c->ob_type->tp_name); + return (long)(Py_UCS4)-1; + } + PyErr_Format(PyExc_TypeError, + "ord() expected a character, but string of length %zd found", size); + return (long)(Py_UCS4)-1; +} + + //////////////////// py_dict_keys.proto //////////////////// static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d); /*proto*/ //////////////////// py_dict_keys //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod1 static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d) { if (PY_MAJOR_VERSION >= 3) - return __Pyx_PyObject_CallMethod1((PyObject*)&PyDict_Type, PYIDENT("keys"), d); + return CALL_UNBOUND_METHOD(PyDict_Type, "keys", d); else return PyDict_Keys(d); } @@ -262,11 +306,10 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d); /*proto*/ //////////////////// py_dict_values //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod1 static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) { if (PY_MAJOR_VERSION >= 3) - return __Pyx_PyObject_CallMethod1((PyObject*)&PyDict_Type, PYIDENT("values"), d); + return CALL_UNBOUND_METHOD(PyDict_Type, "values", d); else return PyDict_Values(d); } @@ -276,11 +319,10 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d); /*proto*/ //////////////////// py_dict_items //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod1 static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) { if (PY_MAJOR_VERSION >= 3) - return __Pyx_PyObject_CallMethod1((PyObject*)&PyDict_Type, PYIDENT("items"), d); + return CALL_UNBOUND_METHOD(PyDict_Type, "items", d); else return PyDict_Items(d); } @@ -290,10 +332,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d); /*proto*/ //////////////////// py_dict_iterkeys //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("keys") : PYIDENT("iterkeys")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "keys", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "iterkeys", d); } //////////////////// py_dict_itervalues.proto //////////////////// @@ -301,10 +345,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d); /*proto*/ //////////////////// py_dict_itervalues //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("values") : PYIDENT("itervalues")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "values", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "itervalues", d); } //////////////////// py_dict_iteritems.proto //////////////////// @@ -312,10 +358,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d); /*proto*/ //////////////////// py_dict_iteritems //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("items") : PYIDENT("iteritems")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "items", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "iteritems", d); } //////////////////// py_dict_viewkeys.proto //////////////////// @@ -326,10 +374,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d); /*proto*/ //////////////////// py_dict_viewkeys //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("keys") : PYIDENT("viewkeys")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "keys", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "viewkeys", d); } //////////////////// py_dict_viewvalues.proto //////////////////// @@ -340,10 +390,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d); /*proto*/ //////////////////// py_dict_viewvalues //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("values") : PYIDENT("viewvalues")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "values", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "viewvalues", d); } //////////////////// py_dict_viewitems.proto //////////////////// @@ -354,10 +406,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d) { static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d); /*proto*/ //////////////////// py_dict_viewitems //////////////////// -//@requires: ObjectHandling.c::PyObjectCallMethod0 static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d) { - return __Pyx_PyObject_CallMethod0(d, (PY_MAJOR_VERSION >= 3) ? PYIDENT("items") : PYIDENT("viewitems")); + if (PY_MAJOR_VERSION >= 3) + return CALL_UNBOUND_METHOD(PyDict_Type, "items", d); + else + return CALL_UNBOUND_METHOD(PyDict_Type, "viewitems", d); } //////////////////// pyfrozenset_new.proto //////////////////// @@ -396,3 +450,34 @@ static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) { return PyObject_Call((PyObject*)&PyFrozenSet_Type, $empty_tuple, NULL); #endif } + + +//////////////////// PySet_Update.proto //////////////////// + +static CYTHON_INLINE int __Pyx_PySet_Update(PyObject* set, PyObject* it); /*proto*/ + +//////////////////// PySet_Update //////////////////// + +static CYTHON_INLINE int __Pyx_PySet_Update(PyObject* set, PyObject* it) { + PyObject *retval; + #if CYTHON_COMPILING_IN_CPYTHON + if (PyAnySet_Check(it)) { + if (PySet_GET_SIZE(it) == 0) + return 0; + // fast and safe case: CPython will update our result set and return it + retval = PySet_Type.tp_as_number->nb_inplace_or(set, it); + if (likely(retval == set)) { + Py_DECREF(retval); + return 0; + } + if (unlikely(!retval)) + return -1; + // unusual result, fall through to set.update() call below + Py_DECREF(retval); + } + #endif + retval = CALL_UNBOUND_METHOD(PySet_Type, "update", set, it); + if (unlikely(!retval)) return -1; + Py_DECREF(retval); + return 0; +} diff --git a/Cython/Utility/CMath.c b/Cython/Utility/CMath.c new file mode 100644 index 0000000000000000000000000000000000000000..3e2e486dca44b84527c895157bd87f9bfc697d0f --- /dev/null +++ b/Cython/Utility/CMath.c @@ -0,0 +1,93 @@ + +/////////////// CDivisionWarning.proto /////////////// + +static int __Pyx_cdivision_warning(const char *, int); /* proto */ + +/////////////// CDivisionWarning /////////////// + +static int __Pyx_cdivision_warning(const char *filename, int lineno) { +#if CYTHON_COMPILING_IN_PYPY + // avoid compiler warnings + filename++; lineno++; + return PyErr_Warn(PyExc_RuntimeWarning, + "division with oppositely signed operands, C and Python semantics differ"); +#else + return PyErr_WarnExplicit(PyExc_RuntimeWarning, + "division with oppositely signed operands, C and Python semantics differ", + filename, + lineno, + __Pyx_MODULE_NAME, + NULL); +#endif +} + + +/////////////// DivInt.proto /////////////// + +static CYTHON_INLINE %(type)s __Pyx_div_%(type_name)s(%(type)s, %(type)s); /* proto */ + +/////////////// DivInt /////////////// + +static CYTHON_INLINE %(type)s __Pyx_div_%(type_name)s(%(type)s a, %(type)s b) { + %(type)s q = a / b; + %(type)s r = a - q*b; + q -= ((r != 0) & ((r ^ b) < 0)); + return q; +} + + +/////////////// ModInt.proto /////////////// + +static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s, %(type)s); /* proto */ + +/////////////// ModInt /////////////// + +static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s a, %(type)s b) { + %(type)s r = a %% b; + r += ((r != 0) & ((r ^ b) < 0)) * b; + return r; +} + + +/////////////// ModFloat.proto /////////////// + +static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s, %(type)s); /* proto */ + +/////////////// ModFloat /////////////// + +static CYTHON_INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s a, %(type)s b) { + %(type)s r = fmod%(math_h_modifier)s(a, b); + r += ((r != 0) & ((r < 0) ^ (b < 0))) * b; + return r; +} + + +/////////////// IntPow.proto /////////////// + +static CYTHON_INLINE %(type)s %(func_name)s(%(type)s, %(type)s); /* proto */ + +/////////////// IntPow /////////////// + +static CYTHON_INLINE %(type)s %(func_name)s(%(type)s b, %(type)s e) { + %(type)s t = b; + switch (e) { + case 3: + t *= b; + case 2: + t *= b; + case 1: + return t; + case 0: + return 1; + } + #if %(signed)s + if (unlikely(e<0)) return 0; + #endif + t = 1; + while (likely(e)) { + t *= (b * (e&1)) | ((~e)&1); /* 1 or b */ + b *= b; + e >>= 1; + } + return t; +} diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 9cc38f03c6580b5058217a45a0df91909b547ff7..05547987446bf6261fb84df60537d1114842c36d 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -245,17 +245,27 @@ __Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op) static int __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { + int result = 0; PyObject *res = op->defaults_getter((PyObject *) op); if (unlikely(!res)) return -1; // Cache result + #if CYTHON_COMPILING_IN_CPYTHON op->defaults_tuple = PyTuple_GET_ITEM(res, 0); Py_INCREF(op->defaults_tuple); op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); Py_INCREF(op->defaults_kwdict); + #else + op->defaults_tuple = PySequence_ITEM(res, 0); + if (unlikely(!op->defaults_tuple)) result = -1; + else { + op->defaults_kwdict = PySequence_ITEM(res, 1); + if (unlikely(!op->defaults_kwdict)) result = -1; + } + #endif Py_DECREF(res); - return 0; + return result; } static int @@ -566,21 +576,21 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) // PyPy does not have this function static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyCFunctionObject* f = (PyCFunctionObject*)func; - PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); + PyCFunction meth = f->m_ml->ml_meth; + PyObject *self = f->m_self; Py_ssize_t size; - switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) { + switch (f->m_ml->ml_flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { case METH_VARARGS: - if (likely(kw == NULL) || PyDict_Size(kw) == 0) + if (likely(kw == NULL || PyDict_Size(kw) == 0)) return (*meth)(self, arg); break; case METH_VARARGS | METH_KEYWORDS: return (*(PyCFunctionWithKeywords)meth)(self, arg, kw); case METH_NOARGS: - if (likely(kw == NULL) || PyDict_Size(kw) == 0) { + if (likely(kw == NULL || PyDict_Size(kw) == 0)) { size = PyTuple_GET_SIZE(arg); - if (size == 0) + if (likely(size == 0)) return (*meth)(self, NULL); PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", @@ -589,10 +599,15 @@ static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject } break; case METH_O: - if (likely(kw == NULL) || PyDict_Size(kw) == 0) { + if (likely(kw == NULL || PyDict_Size(kw) == 0)) { size = PyTuple_GET_SIZE(arg); - if (size == 1) - return (*meth)(self, PyTuple_GET_ITEM(arg, 0)); + if (likely(size == 1)) { + PyObject *result, *arg0 = PySequence_ITEM(arg, 0); + if (unlikely(!arg0)) return NULL; + result = (*meth)(self, arg0); + Py_DECREF(arg0); + return result; + } PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", f->m_ml->ml_name, size); @@ -720,21 +735,30 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, Py } //////////////////// CyFunctionClassCell.proto //////////////////// -static CYTHON_INLINE void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, - PyObject *classobj); +static CYTHON_INLINE int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj); //////////////////// CyFunctionClassCell //////////////////// //@requires: CythonFunction -static CYTHON_INLINE void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) { - int i; +static CYTHON_INLINE int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) { + Py_ssize_t i, count = PyList_GET_SIZE(cyfunctions); - for (i = 0; i < PyList_GET_SIZE(cyfunctions); i++) { - __pyx_CyFunctionObject *m = - (__pyx_CyFunctionObject *) PyList_GET_ITEM(cyfunctions, i); - m->func_classobj = classobj; + for (i = 0; i < count; i++) { + __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) +#if CYTHON_COMPILING_IN_CPYTHON + PyList_GET_ITEM(cyfunctions, i); +#else + PySequence_ITEM(cyfunctions, i); + if (unlikely(!m)) + return -1; +#endif Py_INCREF(classobj); + m->func_classobj = classobj; +#if !CYTHON_COMPILING_IN_CPYTHON + Py_DECREF((PyObject*)m); +#endif } + return 0; } //////////////////// FusedFunction.proto //////////////////// @@ -886,9 +910,15 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx) return NULL; for (i = 0; i < n; i++) { +#if CYTHON_COMPILING_IN_CPYTHON PyObject *item = PyTuple_GET_ITEM(idx, i); - +#else + PyObject *item = PySequence_ITEM(idx, i); +#endif string = _obj_to_str(item); +#if !CYTHON_COMPILING_IN_CPYTHON + Py_DECREF(item); +#endif if (!string || PyList_Append(list, string) < 0) goto __pyx_err; @@ -998,12 +1028,19 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) return NULL; self = binding_func->self; +#if !CYTHON_COMPILING_IN_CPYTHON + Py_INCREF(self); +#endif Py_INCREF(self); PyTuple_SET_ITEM(new_args, 0, self); for (i = 0; i < argc; i++) { +#if CYTHON_COMPILING_IN_CPYTHON PyObject *item = PyTuple_GET_ITEM(args, i); Py_INCREF(item); +#else + PyObject *item = PySequence_ITEM(args, i); if (unlikely(!item)) goto bad; +#endif PyTuple_SET_ITEM(new_args, i + 1, item); } @@ -1014,30 +1051,42 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) PyErr_SetString(PyExc_TypeError, "Need at least one argument, 0 given."); return NULL; } +#if CYTHON_COMPILING_IN_CPYTHON self = PyTuple_GET_ITEM(args, 0); +#else + self = PySequence_ITEM(args, 0); if (unlikely(!self)) return NULL; +#endif } - if (self && !is_classmethod && !is_staticmethod && - !PyObject_IsInstance(self, binding_func->type)) { - PyErr_Format(PyExc_TypeError, - "First argument should be of type %.200s, got %.200s.", - ((PyTypeObject *) binding_func->type)->tp_name, - self->ob_type->tp_name); - goto __pyx_err; + if (self && !is_classmethod && !is_staticmethod) { + int is_instance = PyObject_IsInstance(self, binding_func->type); + if (unlikely(!is_instance)) { + PyErr_Format(PyExc_TypeError, + "First argument should be of type %.200s, got %.200s.", + ((PyTypeObject *) binding_func->type)->tp_name, + self->ob_type->tp_name); + goto bad; + } else if (unlikely(is_instance == -1)) { + goto bad; + } } +#if !CYTHON_COMPILING_IN_CPYTHON + Py_XDECREF(self); + self = NULL; +#endif if (binding_func->__signatures__) { PyObject *tup = PyTuple_Pack(4, binding_func->__signatures__, args, kw == NULL ? Py_None : kw, binding_func->func.defaults_tuple); if (!tup) - goto __pyx_err; + goto bad; new_func = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_callfunction(func, tup, NULL); Py_DECREF(tup); if (!new_func) - goto __pyx_err; + goto bad; Py_XINCREF(binding_func->func.func_classobj); Py_CLEAR(new_func->func.func_classobj); @@ -1047,7 +1096,10 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) } result = __pyx_FusedFunction_callfunction(func, args, kw); -__pyx_err: +bad: +#if !CYTHON_COMPILING_IN_CPYTHON + Py_XDECREF(self); +#endif Py_XDECREF(new_args); Py_XDECREF((PyObject *) new_func); return result; diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 61929406aa39418c27d6aacabbc73db49e82b90b..b28a5d1943cc19b302de9f09b8bf7037fca78a0a 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -144,11 +144,15 @@ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject if (value && PyExceptionInstance_Check(value)) { instance_class = (PyObject*) Py_TYPE(value); if (instance_class != type) { - if (PyObject_IsSubclass(instance_class, type)) { + int is_subclass = PyObject_IsSubclass(instance_class, type); + if (!is_subclass) { + instance_class = NULL; + } else if (unlikely(is_subclass == -1)) { + // error on subclass test + goto bad; + } else { // believe the instance type = instance_class; - } else { - instance_class = NULL; } } } diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index d0ea7b86f95fb2974d5115354013162e2d121930..e2055815d2d8e0b28ab52af79673693808f0f854 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -110,6 +110,17 @@ static void __Pyx_RaiseDoubleKeywordsError( } +//////////////////// RaiseMappingExpected.proto //////////////////// + +static void __Pyx_RaiseMappingExpectedError(PyObject* arg); /*proto*/ + +//////////////////// RaiseMappingExpected //////////////////// + +static void __Pyx_RaiseMappingExpectedError(PyObject* arg) { + PyErr_Format(PyExc_TypeError, "'%.200s' object is not a mapping", Py_TYPE(arg)->tp_name); +} + + //////////////////// KeywordStringCheck.proto //////////////////// static CYTHON_INLINE int __Pyx_CheckKeywordStrings(PyObject *kwdict, const char* function_name, int kw_allowed); /*proto*/ @@ -290,3 +301,58 @@ invalid_keyword: bad: return -1; } + + +//////////////////// MergeKeywords.proto //////////////////// + +static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping); /*proto*/ + +//////////////////// MergeKeywords //////////////////// +//@requires: RaiseDoubleKeywords +//@requires: Optimize.c::dict_iter + +static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { + PyObject *iter, *key = NULL, *value = NULL; + int source_is_dict, result; + Py_ssize_t orig_length, ppos = 0; + + iter = __Pyx_dict_iterator(source_mapping, 0, PYIDENT("items"), &orig_length, &source_is_dict); + if (unlikely(!iter)) { + // slow fallback: try converting to dict, then iterate + PyObject *args; + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; + PyErr_Clear(); + args = PyTuple_Pack(1, source_mapping); + if (likely(args)) { + PyObject *fallback = PyObject_Call((PyObject*)&PyDict_Type, args, NULL); + Py_DECREF(args); + if (likely(fallback)) { + iter = __Pyx_dict_iterator(fallback, 1, PYIDENT("items"), &orig_length, &source_is_dict); + Py_DECREF(fallback); + } + } + if (unlikely(!iter)) goto bad; + } + + while (1) { + result = __Pyx_dict_iter_next(iter, orig_length, &ppos, &key, &value, NULL, source_is_dict); + if (unlikely(result < 0)) goto bad; + if (!result) break; + + if (unlikely(PyDict_Contains(kwdict, key))) { + __Pyx_RaiseDoubleKeywordsError("function", key); + result = -1; + } else { + result = PyDict_SetItem(kwdict, key, value); + } + Py_DECREF(key); + Py_DECREF(value); + if (unlikely(result < 0)) goto bad; + } + Py_XDECREF(iter); + return 0; + +bad: + Py_XDECREF(iter); + return -1; +} diff --git a/Cython/Utility/Generator.c b/Cython/Utility/Generator.c index 03105705790567569d672c4467c1d8aa66d01d9f..589a143bf2bea30e7fb7d8e3598890947678898c 100644 --- a/Cython/Utility/Generator.c +++ b/Cython/Utility/Generator.c @@ -64,6 +64,8 @@ typedef struct { char is_running; } __pyx_GeneratorObject; +static PyTypeObject *__pyx_GeneratorType = 0; + static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body, PyObject *closure, PyObject *name, PyObject *qualname); static int __pyx_Generator_init(void); @@ -80,15 +82,15 @@ static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue); //@requires: Exceptions.c::SwapException //@requires: Exceptions.c::RaiseException //@requires: ObjectHandling.c::PyObjectCallMethod1 +//@requires: ObjectHandling.c::PyObjectGetAttrStr //@requires: CommonTypes.c::FetchCommonType +//@requires: PatchGeneratorABC static PyObject *__Pyx_Generator_Next(PyObject *self); static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value); static PyObject *__Pyx_Generator_Close(PyObject *self); static PyObject *__Pyx_Generator_Throw(PyObject *gen, PyObject *args); -static PyTypeObject *__pyx_GeneratorType = 0; - #define __Pyx_Generator_CheckExact(obj) (Py_TYPE(obj) == __pyx_GeneratorType) #define __Pyx_Generator_Undelegate(gen) Py_CLEAR((gen)->yieldfrom) @@ -102,6 +104,7 @@ static PyTypeObject *__pyx_GeneratorType = 0; static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) { PyObject *et, *ev, *tb; PyObject *value = NULL; + int result; __Pyx_ErrFetch(&et, &ev, &tb); @@ -113,15 +116,21 @@ static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) { return 0; } - if (unlikely(et != PyExc_StopIteration) && - unlikely(!PyErr_GivenExceptionMatches(et, PyExc_StopIteration))) { - __Pyx_ErrRestore(et, ev, tb); - return -1; - } - // most common case: plain StopIteration without or with separate argument if (likely(et == PyExc_StopIteration)) { - if (likely(!ev) || !PyObject_IsInstance(ev, PyExc_StopIteration)) { + int error = 0; +#if PY_VERSION_HEX >= 0x030300A0 + if (ev && Py_TYPE(ev) == (PyTypeObject*)PyExc_StopIteration) { + value = ((PyStopIterationObject *)ev)->value; + Py_INCREF(value); + Py_DECREF(ev); + Py_XDECREF(tb); + Py_DECREF(et); + *pvalue = value; + return 0; + } +#endif + if (!ev || !(error = PyObject_IsInstance(ev, PyExc_StopIteration))) { // PyErr_SetObject() and friends put the value directly into ev if (!ev) { Py_INCREF(Py_None); @@ -132,26 +141,40 @@ static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) { *pvalue = ev; return 0; } + if (unlikely(error == -1)) { + // error during isinstance() check + return -1; + } + } else if (!PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) { + __Pyx_ErrRestore(et, ev, tb); + return -1; } + // otherwise: normalise and check what that gives us PyErr_NormalizeException(&et, &ev, &tb); - if (unlikely(!PyObject_IsInstance(ev, PyExc_StopIteration))) { + result = PyObject_IsInstance(ev, PyExc_StopIteration); + if (unlikely(!result)) { // looks like normalisation failed - raise the new exception __Pyx_ErrRestore(et, ev, tb); return -1; } Py_XDECREF(tb); Py_DECREF(et); + if (unlikely(result == -1)) { + // error during isinstance() check + Py_DECREF(ev); + return -1; + } #if PY_VERSION_HEX >= 0x030300A0 value = ((PyStopIterationObject *)ev)->value; Py_INCREF(value); Py_DECREF(ev); #else { - PyObject* args = PyObject_GetAttr(ev, PYIDENT("args")); + PyObject* args = __Pyx_PyObject_GetAttrStr(ev, PYIDENT("args")); Py_DECREF(ev); if (likely(args)) { - value = PyObject_GetItem(args, 0); + value = PySequence_GetItem(args, 0); Py_DECREF(args); } if (unlikely(!value)) { @@ -260,6 +283,15 @@ PyObject *__Pyx_Generator_SendEx(__pyx_GeneratorObject *self, PyObject *value) { return retval; } +static CYTHON_INLINE +PyObject *__Pyx_Generator_MethodReturn(PyObject *retval) { + if (unlikely(!retval && !PyErr_Occurred())) { + // method call must not terminate with NULL without setting an exception + PyErr_SetNone(PyExc_StopIteration); + } + return retval; +} + static CYTHON_INLINE PyObject *__Pyx_Generator_FinishDelegation(__pyx_GeneratorObject *gen) { PyObject *ret; @@ -295,6 +327,7 @@ static PyObject *__Pyx_Generator_Next(PyObject *self) { } static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) { + PyObject *retval; __pyx_GeneratorObject *gen = (__pyx_GeneratorObject*) self; PyObject *yf = gen->yieldfrom; if (unlikely(__Pyx_Generator_CheckRunning(gen))) @@ -317,9 +350,11 @@ static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) { if (likely(ret)) { return ret; } - return __Pyx_Generator_FinishDelegation(gen); + retval = __Pyx_Generator_FinishDelegation(gen); + } else { + retval = __Pyx_Generator_SendEx(gen, value); } - return __Pyx_Generator_SendEx(gen, value); + return __Pyx_Generator_MethodReturn(retval); } // This helper function is used by gen_close and gen_throw to @@ -335,7 +370,7 @@ static int __Pyx_Generator_CloseIter(__pyx_GeneratorObject *gen, PyObject *yf) { } else { PyObject *meth; gen->is_running = 1; - meth = PyObject_GetAttr(yf, PYIDENT("close")); + meth = __Pyx_PyObject_GetAttrStr(yf, PYIDENT("close")); if (unlikely(!meth)) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_WriteUnraisable(yf); @@ -413,14 +448,14 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) { Py_DECREF(yf); __Pyx_Generator_Undelegate(gen); if (err < 0) - return __Pyx_Generator_SendEx(gen, NULL); + return __Pyx_Generator_MethodReturn(__Pyx_Generator_SendEx(gen, NULL)); goto throw_here; } gen->is_running = 1; if (__Pyx_Generator_CheckExact(yf)) { ret = __Pyx_Generator_Throw(yf, args); } else { - PyObject *meth = PyObject_GetAttr(yf, PYIDENT("throw")); + PyObject *meth = __Pyx_PyObject_GetAttrStr(yf, PYIDENT("throw")); if (unlikely(!meth)) { Py_DECREF(yf); if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { @@ -440,11 +475,11 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) { if (!ret) { ret = __Pyx_Generator_FinishDelegation(gen); } - return ret; + return __Pyx_Generator_MethodReturn(ret); } throw_here: __Pyx_Raise(typ, val, tb, NULL); - return __Pyx_Generator_SendEx(gen, NULL); + return __Pyx_Generator_MethodReturn(__Pyx_Generator_SendEx(gen, NULL)); } static int __Pyx_Generator_traverse(PyObject *self, visitproc visit, void *arg) { @@ -703,7 +738,7 @@ static PyTypeObject __pyx_GeneratorType_type = { static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body, PyObject *closure, PyObject *name, PyObject *qualname) { __pyx_GeneratorObject *gen = - PyObject_GC_New(__pyx_GeneratorObject, &__pyx_GeneratorType_type); + PyObject_GC_New(__pyx_GeneratorObject, __pyx_GeneratorType); if (gen == NULL) return NULL; @@ -739,3 +774,289 @@ static int __pyx_Generator_init(void) { } return 0; } + + +/////////////// ReturnWithStopIteration.proto /////////////// + +#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030500B1 +// CPython 3.3 <= x < 3.5b1 crash in yield-from when the StopIteration is not instantiated +#define __Pyx_ReturnWithStopIteration(value) \ + if (value == Py_None) PyErr_SetNone(PyExc_StopIteration); else __Pyx__ReturnWithStopIteration(value) +static void __Pyx__ReturnWithStopIteration(PyObject* value); /*proto*/ +#else +#define __Pyx_ReturnWithStopIteration(value) PyErr_SetObject(PyExc_StopIteration, value) +#endif + +/////////////// ReturnWithStopIteration /////////////// + +#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030500B1 +static void __Pyx__ReturnWithStopIteration(PyObject* value) { + PyObject *exc, *args; + args = PyTuple_New(1); + if (!args) return; + Py_INCREF(value); + PyTuple_SET_ITEM(args, 0, value); + exc = PyObject_Call(PyExc_StopIteration, args, NULL); + Py_DECREF(args); + if (!exc) return; + Py_INCREF(PyExc_StopIteration); + PyErr_Restore(PyExc_StopIteration, exc, NULL); +} +#endif + + +//////////////////// PatchModuleWithGenerator.proto //////////////////// + +static PyObject* __Pyx_Generator_patch_module(PyObject* module, const char* py_code); /*proto*/ + +//////////////////// PatchModuleWithGenerator //////////////////// +//@substitute: naming + +static PyObject* __Pyx_Generator_patch_module(PyObject* module, const char* py_code) { +#ifdef __Pyx_Generator_USED + PyObject *globals, *result_obj; + globals = PyDict_New(); if (unlikely(!globals)) goto ignore; + if (unlikely(PyDict_SetItemString(globals, "_cython_generator_type", (PyObject*)__pyx_GeneratorType) < 0)) goto ignore; + if (unlikely(PyDict_SetItemString(globals, "_module", module) < 0)) goto ignore; + if (unlikely(PyDict_SetItemString(globals, "__builtins__", $builtins_cname) < 0)) goto ignore; + result_obj = PyRun_String(py_code, Py_file_input, globals, globals); + if (unlikely(!result_obj)) goto ignore; + Py_DECREF(result_obj); + Py_DECREF(globals); + return module; + +ignore: + Py_XDECREF(globals); + PyErr_WriteUnraisable(module); + if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, "Cython module failed to patch module with custom type", 1) < 0)) { + Py_DECREF(module); + module = NULL; + } +#else + // avoid "unused" warning + py_code++; +#endif + return module; +} + + +//////////////////// PatchGeneratorABC.proto //////////////////// + +// patch 'collections.abc' if it lacks generator support +// see https://bugs.python.org/issue24018 +static int __Pyx_patch_abc(void); /*proto*/ + +//////////////////// PatchGeneratorABC //////////////////// +//@requires: PatchModuleWithGenerator + +static int __Pyx_patch_abc(void) { +#if defined(__Pyx_Generator_USED) && (!defined(CYTHON_PATCH_ABC) || CYTHON_PATCH_ABC) + static int abc_patched = 0; + if (!abc_patched) { + PyObject *module; + module = PyImport_ImportModule((PY_VERSION_HEX >= 0x03030000) ? "collections.abc" : "collections"); + if (!module) { + PyErr_WriteUnraisable(NULL); + if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, + ((PY_VERSION_HEX >= 0x03030000) ? + "Cython module failed to patch collections.abc.Generator" : + "Cython module failed to patch collections.Generator"), 1) < 0)) { + return -1; + } + } else { + PyObject *abc = PyObject_GetAttrString(module, "Generator"); + if (abc) { + abc_patched = 1; + Py_DECREF(abc); + } else { + PyErr_Clear(); + module = __Pyx_Generator_patch_module( + module, CSTRING("""\ +def mk_gen(): + from abc import abstractmethod + + required_methods = ( + '__iter__', '__next__' if hasattr(iter(()), '__next__') else 'next', + 'send', 'throw', 'close') + + class Generator(_module.Iterator): + __slots__ = () + + if '__next__' in required_methods: + def __next__(self): + return self.send(None) + else: + def next(self): + return self.send(None) + + @abstractmethod + def send(self, value): + raise StopIteration + + @abstractmethod + def throw(self, typ, val=None, tb=None): + if val is None: + if tb is None: + raise typ + val = typ() + if tb is not None: + val = val.with_traceback(tb) + raise val + + def close(self): + try: + self.throw(GeneratorExit) + except (GeneratorExit, StopIteration): + pass + else: + raise RuntimeError('generator ignored GeneratorExit') + + @classmethod + def __subclasshook__(cls, C): + if cls is Generator: + mro = C.__mro__ + for method in required_methods: + for base in mro: + if method in base.__dict__: + break + else: + return NotImplemented + return True + return NotImplemented + + generator = type((lambda: (yield))()) + Generator.register(generator) + Generator.register(_cython_generator_type) + return Generator + +_module.Generator = mk_gen() +""") + ); + abc_patched = 1; + if (unlikely(!module)) + return -1; + } + Py_DECREF(module); + } + } +#else + // avoid "unused" warning for __Pyx_Generator_patch_module() + if (0) __Pyx_Generator_patch_module(NULL, NULL); +#endif + return 0; +} + + +//////////////////// PatchAsyncIO.proto //////////////////// + +// run after importing "asyncio" to patch Cython generator support into it +static PyObject* __Pyx_patch_asyncio(PyObject* module); /*proto*/ + +//////////////////// PatchAsyncIO //////////////////// +//@requires: PatchModuleWithGenerator +//@requires: PatchInspect + +static PyObject* __Pyx_patch_asyncio(PyObject* module) { +#if defined(__Pyx_Generator_USED) && (!defined(CYTHON_PATCH_ASYNCIO) || CYTHON_PATCH_ASYNCIO) + PyObject *patch_module = NULL; + static int asyncio_patched = 0; + if (unlikely((!asyncio_patched) && module)) { + PyObject *package; + package = __Pyx_Import(PYIDENT("asyncio.coroutines"), NULL, 0); + if (package) { + patch_module = __Pyx_Generator_patch_module( + PyObject_GetAttrString(package, "coroutines"), CSTRING("""\ +old_types = getattr(_module, '_COROUTINE_TYPES', None) +if old_types is not None and _cython_generator_type not in old_types: + _module._COROUTINE_TYPES = type(old_types) (tuple(old_types) + (_cython_generator_type,)) +""") + ); + #if PY_VERSION_HEX < 0x03050000 + } else { + // Py3.4 used to have asyncio.tasks instead of asyncio.coroutines + PyErr_Clear(); + package = __Pyx_Import(PYIDENT("asyncio.tasks"), NULL, 0); + if (unlikely(!package)) goto asyncio_done; + patch_module = __Pyx_Generator_patch_module( + PyObject_GetAttrString(package, "tasks"), CSTRING("""\ +if (hasattr(_module, 'iscoroutine') and + getattr(_module.iscoroutine, '_cython_generator_type', None) is not _cython_generator_type): + def cy_wrap(orig_func, cython_generator_type=_cython_generator_type, type=type): + def cy_iscoroutine(obj): return type(obj) is cython_generator_type or orig_func(obj) + cy_iscoroutine._cython_generator_type = cython_generator_type + return cy_iscoroutine + _module.iscoroutine = cy_wrap(_module.iscoroutine) +""") + ); + #endif + } + Py_DECREF(package); + if (unlikely(!patch_module)) goto ignore; +#if PY_VERSION_HEX < 0x03050000 +asyncio_done: + PyErr_Clear(); +#endif + asyncio_patched = 1; + // now patch inspect.isgenerator() by looking up the imported module in the patched asyncio module + { + PyObject *inspect_module; + if (patch_module) { + inspect_module = PyObject_GetAttrString(patch_module, "inspect"); + Py_DECREF(patch_module); + } else { + inspect_module = __Pyx_Import(PYIDENT("inspect"), NULL, 0); + } + if (unlikely(!inspect_module)) goto ignore; + inspect_module = __Pyx_patch_inspect(inspect_module); + if (unlikely(!inspect_module)) { + Py_DECREF(module); + module = NULL; + } + Py_DECREF(inspect_module); + } + } + return module; +ignore: + PyErr_WriteUnraisable(module); + if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, "Cython module failed to patch asyncio package with custom generator type", 1) < 0)) { + Py_DECREF(module); + module = NULL; + } +#else + // avoid "unused" warning for __Pyx_Generator_patch_module() + if (0) return __Pyx_Generator_patch_module(module, NULL); +#endif + return module; +} + + +//////////////////// PatchInspect.proto //////////////////// + +// run after importing "inspect" to patch Cython generator support into it +static PyObject* __Pyx_patch_inspect(PyObject* module); /*proto*/ + +//////////////////// PatchInspect //////////////////// +//@requires: PatchModuleWithGenerator + +static PyObject* __Pyx_patch_inspect(PyObject* module) { +#if defined(__Pyx_Generator_USED) && (!defined(CYTHON_PATCH_INSPECT) || CYTHON_PATCH_INSPECT) + static int inspect_patched = 0; + if (unlikely((!inspect_patched) && module)) { + module = __Pyx_Generator_patch_module( + module, CSTRING("""\ +if getattr(_module.isgenerator, '_cython_generator_type', None) is not _cython_generator_type: + def cy_wrap(orig_func, cython_generator_type=_cython_generator_type, type=type): + def cy_isgenerator(obj): return type(obj) is cython_generator_type or orig_func(obj) + cy_isgenerator._cython_generator_type = cython_generator_type + return cy_isgenerator + _module.isgenerator = cy_wrap(_module.isgenerator) +""") + ); + inspect_patched = 1; + } +#else + // avoid "unused" warning for __Pyx_Generator_patch_module() + if (0) return __Pyx_Generator_patch_module(module, NULL); +#endif + return module; +} diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 7273d8301a0acde540d968d3ab4ed8f2df652f82..d11ca2adc167f1283c0f997d1b4242f2426eaa46 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -113,6 +113,124 @@ static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) { } +/////////////// ImportStar /////////////// +//@substitute: naming + +/* import_all_from is an unexposed function from ceval.c */ + +static int +__Pyx_import_all_from(PyObject *locals, PyObject *v) +{ + PyObject *all = PyObject_GetAttrString(v, "__all__"); + PyObject *dict, *name, *value; + int skip_leading_underscores = 0; + int pos, err; + + if (all == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; /* Unexpected error */ + PyErr_Clear(); + dict = PyObject_GetAttrString(v, "__dict__"); + if (dict == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_SetString(PyExc_ImportError, + "from-import-* object has no __dict__ and no __all__"); + return -1; + } +#if PY_MAJOR_VERSION < 3 + all = PyObject_CallMethod(dict, (char *)"keys", NULL); +#else + all = PyMapping_Keys(dict); +#endif + Py_DECREF(dict); + if (all == NULL) + return -1; + skip_leading_underscores = 1; + } + + for (pos = 0, err = 0; ; pos++) { + name = PySequence_GetItem(all, pos); + if (name == NULL) { + if (!PyErr_ExceptionMatches(PyExc_IndexError)) + err = -1; + else + PyErr_Clear(); + break; + } + if (skip_leading_underscores && +#if PY_MAJOR_VERSION < 3 + PyString_Check(name) && + PyString_AS_STRING(name)[0] == '_') +#else + PyUnicode_Check(name) && + PyUnicode_AS_UNICODE(name)[0] == '_') +#endif + { + Py_DECREF(name); + continue; + } + value = PyObject_GetAttr(v, name); + if (value == NULL) + err = -1; + else if (PyDict_CheckExact(locals)) + err = PyDict_SetItem(locals, name, value); + else + err = PyObject_SetItem(locals, name, value); + Py_DECREF(name); + Py_XDECREF(value); + if (err != 0) + break; + } + Py_DECREF(all); + return err; +} + + +static int ${import_star}(PyObject* m) { + + int i; + int ret = -1; + char* s; + PyObject *locals = 0; + PyObject *list = 0; +#if PY_MAJOR_VERSION >= 3 + PyObject *utf8_name = 0; +#endif + PyObject *name; + PyObject *item; + + locals = PyDict_New(); if (!locals) goto bad; + if (__Pyx_import_all_from(locals, m) < 0) goto bad; + list = PyDict_Items(locals); if (!list) goto bad; + + for(i=0; i<PyList_GET_SIZE(list); i++) { + name = PyTuple_GET_ITEM(PyList_GET_ITEM(list, i), 0); + item = PyTuple_GET_ITEM(PyList_GET_ITEM(list, i), 1); +#if PY_MAJOR_VERSION >= 3 + utf8_name = PyUnicode_AsUTF8String(name); + if (!utf8_name) goto bad; + s = PyBytes_AS_STRING(utf8_name); + if (${import_star_set}(item, name, s) < 0) goto bad; + Py_DECREF(utf8_name); utf8_name = 0; +#else + s = PyString_AsString(name); + if (!s) goto bad; + if (${import_star_set}(item, name, s) < 0) goto bad; +#endif + } + ret = 0; + +bad: + Py_XDECREF(locals); + Py_XDECREF(list); +#if PY_MAJOR_VERSION >= 3 + Py_XDECREF(utf8_name); +#endif + return ret; +} + + /////////////// ModuleImport.proto /////////////// static PyObject *__Pyx_ImportModule(const char *name); /*proto*/ diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 3ae8a0c71617da62c39b44efe09a1453b49b7258..61bccda9631bbeeb2aac5a82ae70a2f053bf4f9b 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -40,6 +40,10 @@ #define CYTHON_COMPILING_IN_CPYTHON 1 #endif +#if !defined(CYTHON_USE_PYLONG_INTERNALS) && CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070000 +#define CYTHON_USE_PYLONG_INTERNALS 1 +#endif + #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag) #define Py_OptimizeFlag 0 #endif @@ -59,16 +63,16 @@ #define __Pyx_DefaultClassType PyType_Type #endif -#if !defined(Py_TPFLAGS_CHECKTYPES) +#ifndef Py_TPFLAGS_CHECKTYPES #define Py_TPFLAGS_CHECKTYPES 0 #endif -#if !defined(Py_TPFLAGS_HAVE_INDEX) +#ifndef Py_TPFLAGS_HAVE_INDEX #define Py_TPFLAGS_HAVE_INDEX 0 #endif -#if !defined(Py_TPFLAGS_HAVE_NEWBUFFER) +#ifndef Py_TPFLAGS_HAVE_NEWBUFFER #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif -#if !defined(Py_TPFLAGS_HAVE_FINALIZE) +#ifndef Py_TPFLAGS_HAVE_FINALIZE #define Py_TPFLAGS_HAVE_FINALIZE 0 #endif @@ -96,13 +100,14 @@ #if CYTHON_COMPILING_IN_PYPY #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b) #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b) - // PyPy doesn't handle frozenset() in PySet_Size() - #define __Pyx_PyFrozenSet_Size(s) PyObject_Size(s) #else #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b) #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ? \ PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b)) - #define __Pyx_PyFrozenSet_Size(s) PySet_Size(s) +#endif + +#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_Contains) + #define PyUnicode_Contains(u, s) PySequence_Contains(u, s) #endif #define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b)) @@ -257,6 +262,14 @@ class __Pyx_FakeReference { # endif #endif +#ifndef CYTHON_NCP_UNUSED +# if CYTHON_COMPILING_IN_CPYTHON +# define CYTHON_NCP_UNUSED +# else +# define CYTHON_NCP_UNUSED CYTHON_UNUSED +# endif +#endif + typedef struct {PyObject **p; char *s; const Py_ssize_t n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index c35166bb38eb93bc6e3895944af50e307c840fe7..c0680ebde341dc57ad03024ce264e830c7274000 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -279,7 +279,8 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j {{for type in ['List', 'Tuple']}} static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck) { + CYTHON_NCP_UNUSED int wraparound, + CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_COMPILING_IN_CPYTHON if (wraparound & unlikely(i < 0)) i += Py{{type}}_GET_SIZE(o); if ((!boundscheck) || likely((0 <= i) & (i < Py{{type}}_GET_SIZE(o)))) { @@ -294,8 +295,9 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ss } {{endfor}} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, - int is_list, int wraparound, int boundscheck) { +static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list, + CYTHON_NCP_UNUSED int wraparound, + CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_COMPILING_IN_CPYTHON if (is_list || PyList_CheckExact(o)) { Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o); @@ -361,8 +363,8 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyOb return r; } -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, - int is_list, int wraparound, int boundscheck) { +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list, + CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_COMPILING_IN_CPYTHON if (is_list || PyList_CheckExact(o)) { Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o)); @@ -415,7 +417,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObje static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j); static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i, - CYTHON_UNUSED int is_list, int wraparound); + int is_list, int wraparound); /////////////// DelItemInt /////////////// @@ -428,7 +430,7 @@ static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j) { } static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i, - CYTHON_UNUSED int is_list, int wraparound) { + CYTHON_UNUSED int is_list, CYTHON_NCP_UNUSED int wraparound) { #if CYTHON_COMPILING_IN_PYPY if (is_list || PySequence_Check(o)) { return PySequence_DelItem(o, i); @@ -478,12 +480,11 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice( /////////////// SliceObject /////////////// {{if access == 'Get'}} -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice( - PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop, +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(PyObject* obj, {{else}} -static CYTHON_INLINE int __Pyx_PyObject_SetSlice( - PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop, +static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, {{endif}} + Py_ssize_t cstart, Py_ssize_t cstop, PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, int has_cstart, int has_cstop, CYTHON_UNUSED int wraparound) { #if CYTHON_COMPILING_IN_CPYTHON @@ -723,7 +724,11 @@ static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases) { PyObject *metaclass; if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) { PyTypeObject *metatype; +#if CYTHON_COMPILING_IN_CPYTHON PyObject *base = PyTuple_GET_ITEM(bases, 0); +#else + PyObject *base = PySequence_ITEM(bases, 0); +#endif #if PY_MAJOR_VERSION < 3 PyObject* basetype = __Pyx_PyObject_GetAttrStr(base, PYIDENT("__class__")); if (basetype) { @@ -738,6 +743,9 @@ static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases) { metatype = Py_TYPE(base); #endif metaclass = __Pyx_CalculateMetaclass(metatype, bases); +#if !CYTHON_COMPILING_IN_CPYTHON + Py_DECREF(base); +#endif #if PY_MAJOR_VERSION < 3 Py_DECREF(basetype); #endif @@ -762,7 +770,7 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw); /*proto* //@requires: CalculateMetaclass static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) { - PyObject *metaclass = PyDict_GetItem(mkw, PYIDENT("metaclass")); + PyObject *metaclass = mkw ? PyDict_GetItem(mkw, PYIDENT("metaclass")) : NULL; if (metaclass) { Py_INCREF(metaclass); if (PyDict_DelItem(mkw, PYIDENT("metaclass")) < 0) { @@ -927,14 +935,14 @@ static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { /////////////// PyDictContains.proto /////////////// -static CYTHON_INLINE int __Pyx_PyDict_Contains(PyObject* item, PyObject* dict, int eq) { +static CYTHON_INLINE int __Pyx_PyDict_ContainsTF(PyObject* item, PyObject* dict, int eq) { int result = PyDict_Contains(dict, item); return unlikely(result < 0) ? result : (result == (eq == Py_EQ)); } /////////////// PySequenceContains.proto /////////////// -static CYTHON_INLINE int __Pyx_PySequence_Contains(PyObject* item, PyObject* seq, int eq) { +static CYTHON_INLINE int __Pyx_PySequence_ContainsTF(PyObject* item, PyObject* seq, int eq) { int result = PySequence_Contains(seq, item); return unlikely(result < 0) ? result : (result == (eq == Py_EQ)); } @@ -1093,6 +1101,127 @@ static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr #endif +/////////////// UnpackUnboundCMethod.proto /////////////// + +typedef struct { + PyObject *type; + PyObject **method_name; + PyCFunction func; // set on first access (direct C function pointer) + PyObject *method; // set on first access (fallback) + int flag; +} __Pyx_CachedCFunction; + +/////////////// UnpackUnboundCMethod /////////////// +//@requires: PyObjectGetAttrStr + +static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) { + PyObject *method; + method = __Pyx_PyObject_GetAttrStr(target->type, *target->method_name); + if (unlikely(!method)) + return -1; + target->method = method; +#if CYTHON_COMPILING_IN_CPYTHON + #if PY_MAJOR_VERSION >= 3 + // method dscriptor type isn't exported in Py2.x, cannot easily check the type there + if (likely(PyObject_TypeCheck(method, &PyMethodDescr_Type))) + #endif + { + PyMethodDescrObject *descr = (PyMethodDescrObject*) method; + target->func = descr->d_method->ml_meth; + target->flag = descr->d_method->ml_flags & (METH_VARARGS | METH_KEYWORDS | METH_O | METH_NOARGS); + } +#endif + return 0; +} + + +/////////////// CallUnboundCMethod0.proto /////////////// +//@substitute: naming + +static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self); /*proto*/ +#if CYTHON_COMPILING_IN_CPYTHON +#define __Pyx_CallUnboundCMethod0(cfunc, self) \ + ((likely((cfunc)->func)) ? \ + (likely((cfunc)->flag == METH_NOARGS) ? (*((cfunc)->func))(self, NULL) : \ + (likely((cfunc)->flag == (METH_VARARGS | METH_KEYWORDS)) ? ((*(PyCFunctionWithKeywords)(cfunc)->func)(self, $empty_tuple, NULL)) : \ + ((cfunc)->flag == METH_VARARGS ? (*((cfunc)->func))(self, $empty_tuple) : __Pyx__CallUnboundCMethod0(cfunc, self)))) : \ + __Pyx__CallUnboundCMethod0(cfunc, self)) +#else +#define __Pyx_CallUnboundCMethod0(cfunc, self) __Pyx__CallUnboundCMethod0(cfunc, self) +#endif + +/////////////// CallUnboundCMethod0 /////////////// +//@requires: UnpackUnboundCMethod +//@requires: PyObjectCall + +static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self) { + PyObject *args, *result = NULL; + if (unlikely(!cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; +#if CYTHON_COMPILING_IN_CPYTHON + args = PyTuple_New(1); + if (unlikely(!args)) goto bad; + Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); +#else + args = PyTuple_Pack(1, self); + if (unlikely(!args)) goto bad; +#endif + result = __Pyx_PyObject_Call(cfunc->method, args, NULL); + Py_DECREF(args); +bad: + return result; +} + + +/////////////// CallUnboundCMethod1.proto /////////////// + +static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg); /*proto*/ + +#if CYTHON_COMPILING_IN_CPYTHON +#define __Pyx_CallUnboundCMethod1(cfunc, self, arg) \ + ((likely((cfunc)->func && (cfunc)->flag == METH_O)) ? (*((cfunc)->func))(self, arg) : \ + __Pyx__CallUnboundCMethod1(cfunc, self, arg)) +#else +#define __Pyx_CallUnboundCMethod1(cfunc, self, arg) __Pyx__CallUnboundCMethod1(cfunc, self, arg) +#endif + +/////////////// CallUnboundCMethod1 /////////////// +//@requires: UnpackUnboundCMethod +//@requires: PyObjectCall + +static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg){ + PyObject *args, *result = NULL; + if (unlikely(!cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; +#if CYTHON_COMPILING_IN_CPYTHON + if (cfunc->func && (cfunc->flag & METH_VARARGS)) { + args = PyTuple_New(1); + if (unlikely(!args)) goto bad; + Py_INCREF(arg); + PyTuple_SET_ITEM(args, 0, arg); + if (cfunc->flag & METH_KEYWORDS) + result = (*(PyCFunctionWithKeywords)cfunc->func)(self, args, NULL); + else + result = (*cfunc->func)(self, args); + } else { + args = PyTuple_New(2); + if (unlikely(!args)) goto bad; + Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); + Py_INCREF(arg); + PyTuple_SET_ITEM(args, 1, arg); + result = __Pyx_PyObject_Call(cfunc->method, args, NULL); + } +#else + args = PyTuple_Pack(2, self, arg); + if (unlikely(!args)) goto bad; + result = __Pyx_PyObject_Call(cfunc->method, args, NULL); +#endif +bad: + Py_XDECREF(args); + return result; +} + + /////////////// PyObjectCallMethod0.proto /////////////// static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); /*proto*/ @@ -1414,6 +1543,8 @@ bad: static PyObject* __Pyx__PyNumber_MatrixMultiply(PyObject* x, PyObject* y, const char* op_name) { int right_is_subtype = PyObject_IsSubclass((PyObject*)Py_TYPE(y), (PyObject*)Py_TYPE(x)); + if (unlikely(right_is_subtype == -1)) + return NULL; if (right_is_subtype) { // to allow subtypes to override parent behaviour, try reversed operation first // see note at https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 57a38e4e0ac7a763d0c968ede1a0ff804b9bef34..9b18bec1a59a2b551038f8b8322941bb2a5a755c 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -78,11 +78,17 @@ static CYTHON_INLINE int __Pyx_PyList_Extend(PyObject* L, PyObject* v) { /////////////// pop.proto /////////////// -#define __Pyx_PyObject_Pop(L) (PyList_CheckExact(L) ? \ - __Pyx_PyList_Pop(L) : __Pyx__PyObject_Pop(L)) +static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L); /*proto*/ +#if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L); /*proto*/ -static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L); /*proto*/ +#define __Pyx_PyObject_Pop(L) (likely(PyList_CheckExact(L)) ? \ + __Pyx_PyList_Pop(L) : __Pyx__PyObject_Pop(L)) + +#else +#define __Pyx_PyList_Pop(L) __Pyx__PyObject_Pop(L) +#define __Pyx_PyObject_Pop(L) __Pyx__PyObject_Pop(L) +#endif /////////////// pop /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod0 @@ -96,44 +102,65 @@ static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) { return __Pyx_PyObject_CallMethod0(L, PYIDENT("pop")); } -static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { #if CYTHON_COMPILING_IN_CPYTHON +static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { /* Check that both the size is positive and no reallocation shrinking needs to be done. */ if (likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) { Py_SIZE(L) -= 1; return PyList_GET_ITEM(L, PyList_GET_SIZE(L)); } -#endif - return __Pyx_PyObject_CallMethod0(L, PYIDENT("pop")); + return CALL_UNBOUND_METHOD(PyList_Type, "pop", L); } +#endif /////////////// pop_index.proto /////////////// -#define __Pyx_PyObject_PopIndex(L, ix, is_signed, type, to_py_func) ( \ - (PyList_CheckExact(L) && __Pyx_fits_Py_ssize_t(ix, type, is_signed)) ? \ - __Pyx__PyList_PopIndex(L, ix) : __Pyx__PyObject_PopIndex(L, to_py_func(ix))) +static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix); /*proto*/ +static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/ + +#if CYTHON_COMPILING_IN_CPYTHON +static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t ix); /*proto*/ + +#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \ + (likely(PyList_CheckExact(L) && __Pyx_fits_Py_ssize_t(ix, type, is_signed))) ? \ + __Pyx__PyList_PopIndex(L, py_ix, ix) : ( \ + (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \ + __Pyx__PyObject_PopIndex(L, py_ix))) -#define __Pyx_PyList_PopIndex(L, ix, is_signed, type, to_py_func) ( \ +#define __Pyx_PyList_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \ __Pyx_fits_Py_ssize_t(ix, type, is_signed) ? \ - __Pyx__PyList_PopIndex(L, ix) : __Pyx__PyObject_PopIndex(L, to_py_func(ix))) + __Pyx__PyList_PopIndex(L, py_ix, ix) : ( \ + (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \ + __Pyx__PyObject_PopIndex(L, py_ix))) -static PyObject* __Pyx__PyList_PopIndex(PyObject* L, Py_ssize_t ix); /*proto*/ -static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/ +#else + +#define __Pyx_PyList_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) \ + __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) + +#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \ + (unlikely(py_ix == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \ + __Pyx__PyObject_PopIndex(L, py_ix)) +#endif /////////////// pop_index /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod1 -static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix) { +static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix) { PyObject *r; if (unlikely(!py_ix)) return NULL; - r = __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix); + r = __Pyx__PyObject_PopIndex(L, py_ix); Py_DECREF(py_ix); return r; } -static PyObject* __Pyx__PyList_PopIndex(PyObject* L, Py_ssize_t ix) { +static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix) { + return __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix); +} + #if CYTHON_COMPILING_IN_CPYTHON +static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t ix) { Py_ssize_t size = PyList_GET_SIZE(L); if (likely(size > (((PyListObject*)L)->allocated >> 1))) { Py_ssize_t cix = ix; @@ -148,9 +175,13 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, Py_ssize_t ix) { return v; } } -#endif - return __Pyx__PyObject_PopIndex(L, PyInt_FromSsize_t(ix)); + if (py_ix == Py_None) { + return __Pyx__PyObject_PopNewIndex(L, PyInt_FromSsize_t(ix)); + } else { + return __Pyx__PyObject_PopIndex(L, py_ix); + } } +#endif /////////////// dict_getitem_default.proto /////////////// @@ -280,8 +311,9 @@ static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* iterable, int is_di return PyObject_GetIter(iterable); } -static CYTHON_INLINE int __Pyx_dict_iter_next(PyObject* iter_obj, Py_ssize_t orig_length, Py_ssize_t* ppos, - PyObject** pkey, PyObject** pvalue, PyObject** pitem, int source_is_dict) { +static CYTHON_INLINE int __Pyx_dict_iter_next( + PyObject* iter_obj, CYTHON_NCP_UNUSED Py_ssize_t orig_length, CYTHON_NCP_UNUSED Py_ssize_t* ppos, + PyObject** pkey, PyObject** pvalue, PyObject** pitem, int source_is_dict) { PyObject* next_item; #if !CYTHON_COMPILING_IN_PYPY if (source_is_dict) { @@ -390,7 +422,7 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj); /* proto */ static double __Pyx__PyObject_AsDouble(PyObject* obj) { PyObject* float_value; #if CYTHON_COMPILING_IN_PYPY - float_value = PyNumber_Float(obj); + float_value = PyNumber_Float(obj); if (0) goto bad; #else PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number; if (likely(nb) && likely(nb->nb_float)) { @@ -442,22 +474,27 @@ static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject // see http://bugs.python.org/issue21420 #if CYTHON_COMPILING_IN_CPYTHON Py_ssize_t shiftby; +#if PY_MAJOR_VERSION < 3 + if (likely(PyInt_CheckExact(exp))) { + shiftby = PyInt_AS_LONG(exp); + } else +#endif if (likely(PyLong_CheckExact(exp))) { - #if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS - switch (Py_SIZE(exp)) { - case 0: shiftby = 0; break; - case 1: shiftby = ((PyLongObject*)exp)->ob_digit[0]; break; - default: - if (unlikely(Py_SIZE(exp) < 0)) goto fallback; - shiftby = PyLong_AsSsize_t(exp); break; + #if CYTHON_USE_PYLONG_INTERNALS + const Py_ssize_t size = Py_SIZE(exp); + // tuned to optimise branch prediction + if (likely(size == 1)) { + shiftby = ((PyLongObject*)exp)->ob_digit[0]; + } else if (size == 0) { + return PyInt_FromLong(1L); + } else if (unlikely(size < 0)) { + goto fallback; + } else { + shiftby = PyLong_AsSsize_t(exp); } #else shiftby = PyLong_AsSsize_t(exp); #endif -#if PY_MAJOR_VERSION < 3 - } else if (likely(PyInt_CheckExact(exp))) { - shiftby = PyInt_AsLong(exp); -#endif } else { goto fallback; } @@ -465,6 +502,9 @@ static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject if ((size_t)shiftby <= sizeof(long) * 8 - 2) { long value = 1L << shiftby; return PyInt_FromLong(value); + } else if ((size_t)shiftby <= sizeof(unsigned PY_LONG_LONG) * 8 - 1) { + unsigned PY_LONG_LONG value = ((unsigned PY_LONG_LONG)1) << shiftby; + return PyLong_FromUnsignedLongLong(value); } else { PyObject *one = PyInt_FromLong(1L); if (unlikely(!one)) return NULL; @@ -485,54 +525,210 @@ fallback: static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/ #else #define __Pyx_PyInt_{{op}}{{order}}(op1, op2, intval, inplace) \ - ((inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2)) + {{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}}) + {{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2)) + {{endif}} #endif /////////////// PyIntBinop /////////////// //@requires: TypeConversion.c::PyLongInternals #if CYTHON_COMPILING_IN_CPYTHON +{{py: from Cython.Utility import pylong_join }} {{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} - -static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace) { - const long {{'a' if order == 'CObj' else 'b'}} = intval; +{{py: slot_name = {'TrueDivide': 'true_divide', 'FloorDivide': 'floor_divide'}.get(op, op.lower()) }} +{{py: +c_op = { + 'Add': '+', 'Subtract': '-', 'Remainder': '%', 'TrueDivide': '/', 'FloorDivide': '/', + 'Or': '|', 'Xor': '^', 'And': '&', 'Rshift': '>>', + 'Eq': '==', 'Ne': '!=', + }[op] +}} + +static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) { + {{if op in ('Eq', 'Ne')}} + if (op1 == op2) { + Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}}; + } + {{endif}} #if PY_MAJOR_VERSION < 3 if (likely(PyInt_CheckExact({{pyval}}))) { - long x, {{ival}}; - {{ival}} = PyInt_AS_LONG({{pyval}}); - // adapted from intobject.c in Py2.7: - // casts in the line below avoid undefined behaviour on overflow - x = (long)((unsigned long)a {{ '+' if op == 'Add' else '-' }} b); - if (likely((x^a) >= 0 || (x^{{ '~' if op == 'Subtract' else '' }}b) >= 0)) + const long {{'a' if order == 'CObj' else 'b'}} = intval; + {{if c_op in '+-%' or op == 'FloorDivide'}} + long x; + {{endif}} + long {{ival}} = PyInt_AS_LONG({{pyval}}); + + {{if op in ('Eq', 'Ne')}} + if (a {{c_op}} b) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + {{elif c_op in '+-'}} + // adapted from intobject.c in Py2.7: + // casts in the line below avoid undefined behaviour on overflow + x = (long)((unsigned long)a {{c_op}} b); + if (likely((x^a) >= 0 || (x^{{ '~' if op == 'Subtract' else '' }}b) >= 0)) + return PyInt_FromLong(x); + return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + {{elif c_op == '%'}} + // see ExprNodes.py :: mod_int_utility_code + x = a % b; + x += ((x != 0) & ((x ^ b) < 0)) * b; return PyInt_FromLong(x); - return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2); + {{elif op == 'TrueDivide'}} + if (8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= (1L << 53))) { + return PyFloat_FromDouble((double)a / (double)b); + } + // let Python do the rounding + return PyInt_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + {{elif op == 'FloorDivide'}} + // INT_MIN / -1 is the only case that overflows + if (unlikely(b == -1 && ((unsigned long)a) == 0-(unsigned long)a)) + return PyInt_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + else { + long q, r; + // see ExprNodes.py :: div_int_utility_code + q = a / b; + r = a - q*b; + q -= ((r != 0) & ((r ^ b) < 0)); + x = q; + } + return PyInt_FromLong(x); + {{else}} + // other operations are safe, no overflow + return PyInt_FromLong(a {{c_op}} b); + {{endif}} } #endif - #if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS + #if CYTHON_USE_PYLONG_INTERNALS && PY_MAJOR_VERSION >= 3 if (likely(PyLong_CheckExact({{pyval}}))) { - long {{ival}}; - switch (Py_SIZE({{pyval}})) { - case -1: {{ival}} = -(sdigit)((PyLongObject*){{pyval}})->ob_digit[0]; break; - case 0: {{ival}} = 0; break; - case 1: {{ival}} = ((PyLongObject*){{pyval}})->ob_digit[0]; break; - default: return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2); + const long {{'a' if order == 'CObj' else 'b'}} = intval; + long {{ival}}{{if op not in ('Eq', 'Ne')}}, x{{endif}}; + {{if op not in ('Eq', 'Ne', 'TrueDivide')}} + const PY_LONG_LONG ll{{'a' if order == 'CObj' else 'b'}} = intval; + PY_LONG_LONG ll{{ival}}, llx; + {{endif}} + const digit* digits = ((PyLongObject*){{pyval}})->ob_digit; + const Py_ssize_t size = Py_SIZE({{pyval}}); + // handle most common case first to avoid indirect branch and optimise branch prediction + if (likely(__Pyx_sst_abs(size) <= 1)) { + {{ival}} = likely(size) ? digits[0] : 0; + if (size == -1) {{ival}} = -{{ival}}; + } else { + switch (size) { + {{for _size in range(2, 5)}} + {{for _case in (-_size, _size)}} + case {{_case}}: + if (8 * sizeof(long) - 1 > {{_size}} * PyLong_SHIFT{{if op == 'TrueDivide'}} && {{_size-1}} * PyLong_SHIFT < 53{{endif}}) { + {{ival}} = {{'-' if _case < 0 else ''}}(long) {{pylong_join(_size, 'digits')}}; + break; + {{if op not in ('Eq', 'Ne', 'TrueDivide')}} + } else if (8 * sizeof(PY_LONG_LONG) - 1 > {{_size}} * PyLong_SHIFT) { + ll{{ival}} = {{'-' if _case < 0 else ''}}(PY_LONG_LONG) {{pylong_join(_size, 'digits', 'unsigned PY_LONG_LONG')}}; + goto long_long; + {{endif}} + } + // if size doesn't fit into a long or PY_LONG_LONG anymore, fall through to default + {{endfor}} + {{endfor}} + + {{if op in ('Eq', 'Ne')}} + #if PyLong_SHIFT < 30 && PyLong_SHIFT != 15 + // unusual setup - your fault + default: return PyLong_Type.tp_richcompare({{'op1, op2' if order == 'ObjC' else 'op2, op1'}}, Py_{{op.upper()}}); + #else + // too large for the long values we allow => definitely not equal + default: Py_RETURN_{{'FALSE' if op == 'Eq' else 'TRUE'}}; + #endif + {{else}} + default: return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + {{endif}} + } } - return PyLong_FromLong(a {{ '+' if op == 'Add' else '-' }} b); + {{if op in ('Eq', 'Ne')}} + if (a {{c_op}} b) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + {{else}} + {{if c_op == '%'}} + // see ExprNodes.py :: mod_int_utility_code + x = a % b; + x += ((x != 0) & ((x ^ b) < 0)) * b; + {{elif op == 'TrueDivide'}} + if (8 * sizeof(long) <= 53 || (abs(size) <= 52 / PyLong_SHIFT) || likely(labs({{ival}}) <= (1L << 53))) { + return PyFloat_FromDouble((double)a / (double)b); + } + return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + {{elif op == 'FloorDivide'}} + { + long q, r; + // see ExprNodes.py :: div_int_utility_code + q = a / b; + r = a - q*b; + q -= ((r != 0) & ((r ^ b) < 0)); + x = q; + } + {{else}} + x = a {{c_op}} b; + {{endif}} + return PyLong_FromLong(x); + + {{if op != 'TrueDivide'}} + long_long: + {{if c_op == '%'}} + // see ExprNodes.py :: mod_int_utility_code + llx = lla % llb; + llx += ((llx != 0) & ((llx ^ llb) < 0)) * llb; + {{elif op == 'FloorDivide'}} + { + PY_LONG_LONG q, r; + // see ExprNodes.py :: div_int_utility_code + q = lla / llb; + r = lla - q*llb; + q -= ((r != 0) & ((r ^ llb) < 0)); + llx = q; + } + {{else}} + llx = lla {{c_op}} llb; + {{endif}} + return PyLong_FromLongLong(llx); + {{endif}} + {{endif}} } #endif + {{if c_op in '+-' or op in ('TrueDivide', 'Eq', 'Ne')}} if (PyFloat_CheckExact({{pyval}})) { - double result; + const long {{'a' if order == 'CObj' else 'b'}} = intval; double {{ival}} = PyFloat_AS_DOUBLE({{pyval}}); - // copied from floatobject.c in Py3.5: - PyFPE_START_PROTECT("{{op.lower()}}", return NULL) - result = ((double)a) {{ '+' if op == 'Add' else '-' }} (double)b; - PyFPE_END_PROTECT(result) - return PyFloat_FromDouble(result); + {{if op in ('Eq', 'Ne')}} + if ((double)a {{c_op}} (double)b) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + {{else}} + double result; + // copied from floatobject.c in Py3.5: + PyFPE_START_PROTECT("{{op.lower() if not op.endswith('Divide') else 'divide'}}", return NULL) + result = ((double)a) {{c_op}} (double)b; + PyFPE_END_PROTECT(result) + return PyFloat_FromDouble(result); + {{endif}} } + {{endif}} + + {{if op in ('Eq', 'Ne')}} + return PyObject_RichCompare(op1, op2, Py_{{op.upper()}}); + {{else}} return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); + {{endif}} } #endif @@ -542,18 +738,34 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/ #else #define __Pyx_PyFloat_{{op}}{{order}}(op1, op2, floatval, inplace) \ - ((inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2)) + {{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}}) + {{elif op == 'Divide'}}((inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2))) + {{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2)) + {{endif}} #endif /////////////// PyFloatBinop /////////////// //@requires: TypeConversion.c::PyLongInternals #if CYTHON_COMPILING_IN_CPYTHON +{{py: from Cython.Utility import pylong_join }} {{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} - -static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace) { +{{py: +c_op = { + 'Add': '+', 'Subtract': '-', 'TrueDivide': '/', 'Divide': '/', 'Remainder': '%', + 'Eq': '==', 'Ne': '!=', + }[op] +}} + +static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, CYTHON_UNUSED int inplace) { const double {{'a' if order == 'CObj' else 'b'}} = floatval; - double result, {{fval}}; + double {{fval}}{{if op not in ('Eq', 'Ne')}}, result{{endif}}; + + {{if op in ('Eq', 'Ne')}} + if (op1 == op2) { + Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}}; + } + {{endif}} if (likely(PyFloat_CheckExact({{pyval}}))) { {{fval}} = PyFloat_AS_DOUBLE({{pyval}}); @@ -566,26 +778,72 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou #endif if (likely(PyLong_CheckExact({{pyval}}))) { - #if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS - switch (Py_SIZE({{pyval}})) { - case -1: {{fval}} = -(double)((PyLongObject*){{pyval}})->ob_digit[0]; break; + #if CYTHON_USE_PYLONG_INTERNALS && PY_MAJOR_VERSION >= 3 + const digit* digits = ((PyLongObject*){{pyval}})->ob_digit; + const Py_ssize_t size = Py_SIZE({{pyval}}); + switch (size) { case 0: {{fval}} = 0.0; break; - case 1: {{fval}} = (double)((PyLongObject*){{pyval}})->ob_digit[0]; break; - default: {{fval}} = PyLong_AsDouble({{pyval}}); - if (unlikely({{fval}} == -1 && PyErr_Occurred())) return NULL; - break; - } + case -1: {{fval}} = -(double) digits[0]; break; + case 1: {{fval}} = (double) digits[0]; break; + {{for _size in (2, 3, 4)}} + case -{{_size}}: + case {{_size}}: + if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || ({{_size-1}} * PyLong_SHIFT < 53))) { + {{fval}} = (double) {{pylong_join(_size, 'digits')}}; + // let CPython do its own float rounding from 2**53 on (max. consecutive integer in double float) + if ((8 * sizeof(unsigned long) < 53) || ({{_size}} * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) { + if (size == {{-_size}}) + {{fval}} = -{{fval}}; + break; + } + } + // Fall through if size doesn't fit safely into a double anymore. + // It may not be obvious that this is a safe fall-through given the "fval < 2**53" + // check above. However, the number of digits that CPython uses for a given PyLong + // value is minimal, and together with the "(size-1) * SHIFT < 53" check above, + // this should make it safe. + {{endfor}} + default: #else - {{fval}} = PyLong_AsDouble({{pyval}}); - if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL; + { #endif - } else + {{if op in ('Eq', 'Ne')}} + return PyFloat_Type.tp_richcompare({{'op1, op2' if order == 'CObj' else 'op2, op1'}}, Py_{{op.upper()}}); + {{else}} + {{fval}} = PyLong_AsDouble({{pyval}}); + if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL; + {{endif}} + } + } else { + {{if op in ('Eq', 'Ne')}} + return PyObject_RichCompare(op1, op2, Py_{{op.upper()}}); + {{elif op == 'Divide'}} + return (inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2)); + {{else}} return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); + {{endif}} + } - // copied from floatobject.c in Py3.5: - PyFPE_START_PROTECT("{{op.lower()}}", return NULL) - result = a {{ '+' if op == 'Add' else '-' }} b; - PyFPE_END_PROTECT(result) - return PyFloat_FromDouble(result); + {{if op in ('Eq', 'Ne')}} + if (a {{c_op}} b) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + {{else}} + // copied from floatobject.c in Py3.5: + PyFPE_START_PROTECT("{{op.lower() if not op.endswith('Divide') else 'divide'}}", return NULL) + {{if c_op == '%'}} + result = fmod(a, b); + if (result) + result += ((result < 0) ^ (b < 0)) * b; + else + result = copysign(0.0, b); + {{else}} + result = a {{c_op}} b; + {{endif}} + PyFPE_END_PROTECT(result) + return PyFloat_FromDouble(result); + {{endif}} } #endif diff --git a/Cython/Utility/Overflow.c b/Cython/Utility/Overflow.c index 1ac58df5b25fd34f3b25b68b8bedc78e0e36640b..b357df99fe179e1a55a948537d8b7e8d881f4301 100644 --- a/Cython/Utility/Overflow.c +++ b/Cython/Utility/Overflow.c @@ -83,8 +83,8 @@ static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, { {{UINT}} r = ({{UINT}}) big_r; *overflow |= big_r != r; return r; - } else if (sizeof({{UINT}}) < sizeof(unsigned long long)) { - unsigned long long big_r = ((unsigned long long) a) * ((unsigned long long) b); + } else if (sizeof({{UINT}}) < sizeof(unsigned PY_LONG_LONG)) { + unsigned PY_LONG_LONG big_r = ((unsigned PY_LONG_LONG) a) * ((unsigned PY_LONG_LONG) b); {{UINT}} r = ({{UINT}}) big_r; *overflow |= big_r != r; return r; @@ -137,8 +137,8 @@ static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{I {{INT}} r = ({{INT}}) big_r; *overflow |= big_r != r; return r; - } else if (sizeof({{INT}}) < sizeof(long long)) { - long long big_r = ((long long) a) + ((long long) b); + } else if (sizeof({{INT}}) < sizeof(PY_LONG_LONG)) { + PY_LONG_LONG big_r = ((PY_LONG_LONG) a) + ((PY_LONG_LONG) b); {{INT}} r = ({{INT}}) big_r; *overflow |= big_r != r; return r; @@ -181,8 +181,8 @@ static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{I {{INT}} r = ({{INT}}) big_r; *overflow |= big_r != r; return ({{INT}}) r; - } else if (sizeof({{INT}}) < sizeof(long long)) { - long long big_r = ((long long) a) * ((long long) b); + } else if (sizeof({{INT}}) < sizeof(PY_LONG_LONG)) { + PY_LONG_LONG big_r = ((PY_LONG_LONG) a) * ((PY_LONG_LONG) b); {{INT}} r = ({{INT}}) big_r; *overflow |= big_r != r; return ({{INT}}) r; @@ -228,7 +228,7 @@ __Pyx_check_sane_{{NAME}}(); static int __Pyx_check_sane_{{NAME}}(void) { if (sizeof({{TYPE}}) <= sizeof(int) || sizeof({{TYPE}}) == sizeof(long) || - sizeof({{TYPE}}) == sizeof(long long)) { + sizeof({{TYPE}}) == sizeof(PY_LONG_LONG)) { return 0; } else { PyErr_Format(PyExc_RuntimeError, \ @@ -252,7 +252,7 @@ static CYTHON_INLINE {{TYPE}} __Pyx_{{BINOP}}_{{NAME}}_checking_overflow({{TYPE} return __Pyx_{{BINOP}}_unsigned_int_checking_overflow(a, b, overflow); } else if (sizeof({{TYPE}}) == sizeof(unsigned long)) { return __Pyx_{{BINOP}}_unsigned_long_checking_overflow(a, b, overflow); - } else if (sizeof({{TYPE}}) == sizeof(unsigned long long)) { + } else if (sizeof({{TYPE}}) == sizeof(unsigned PY_LONG_LONG)) { return __Pyx_{{BINOP}}_unsigned_long_long_checking_overflow(a, b, overflow); } else { abort(); return 0; // handled elsewhere @@ -262,7 +262,7 @@ static CYTHON_INLINE {{TYPE}} __Pyx_{{BINOP}}_{{NAME}}_checking_overflow({{TYPE} return __Pyx_{{BINOP}}_int_checking_overflow(a, b, overflow); } else if (sizeof({{TYPE}}) == sizeof(long)) { return __Pyx_{{BINOP}}_long_checking_overflow(a, b, overflow); - } else if (sizeof({{TYPE}}) == sizeof(long long)) { + } else if (sizeof({{TYPE}}) == sizeof(PY_LONG_LONG)) { return __Pyx_{{BINOP}}_long_long_checking_overflow(a, b, overflow); } else { abort(); return 0; // handled elsewhere @@ -282,3 +282,10 @@ static CYTHON_INLINE {{TYPE}} __Pyx_lshift_{{NAME}}_checking_overflow({{TYPE}} a } #define __Pyx_lshift_const_{{NAME}}_checking_overflow __Pyx_lshift_{{NAME}}_checking_overflow + +/////////////// UnaryNegOverflows.proto /////////////// + +//FIXME: shouldn't the macro name be prefixed by "__Pyx_" ? Too late now, I guess... +// from intobject.c +#define UNARY_NEG_WOULD_OVERFLOW(x) \ + (((x) < 0) & ((unsigned long)(x) == 0-(unsigned long)(x))) diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index ad5c4fcd75388172250bd906c44d9b38ab2e1657..06598745d332d0b2044760360eb3b73aff441731 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -111,12 +111,24 @@ static CYTHON_INLINE int __Pyx_PyUnicodeBufferContainsUCS4(Py_UNICODE* buffer, P //////////////////// PyUnicodeContains.proto //////////////////// -static CYTHON_INLINE int __Pyx_PyUnicode_Contains(PyObject* substring, PyObject* text, int eq) { +static CYTHON_INLINE int __Pyx_PyUnicode_ContainsTF(PyObject* substring, PyObject* text, int eq) { int result = PyUnicode_Contains(text, substring); return unlikely(result < 0) ? result : (result == (eq == Py_EQ)); } +//////////////////// CStringEquals.proto //////////////////// + +static CYTHON_INLINE int __Pyx_StrEq(const char *, const char *); /*proto*/ + +//////////////////// CStringEquals //////////////////// + +static CYTHON_INLINE int __Pyx_StrEq(const char *s1, const char *s2) { + while (*s1 != '\0' && *s1 == *s2) { s1++; s2++; } + return *s1 == *s2; +} + + //////////////////// StrEquals.proto //////////////////// //@requires: BytesEquals //@requires: UnicodeEquals @@ -533,7 +545,7 @@ static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr, result = PyUnicode_Tailmatch(s, PyTuple_GET_ITEM(substr, i), start, end, direction); #else - PyObject* sub = PySequence_GetItem(substr, i); + PyObject* sub = PySequence_ITEM(substr, i); if (unlikely(!sub)) return -1; result = PyUnicode_Tailmatch(s, sub, start, end, direction); Py_DECREF(sub); @@ -618,7 +630,7 @@ static int __Pyx_PyBytes_Tailmatch(PyObject* self, PyObject* substr, Py_ssize_t result = __Pyx_PyBytes_SingleTailmatch(self, PyTuple_GET_ITEM(substr, i), start, end, direction); #else - PyObject* sub = PySequence_GetItem(substr, i); + PyObject* sub = PySequence_ITEM(substr, i); if (unlikely(!sub)) return -1; result = __Pyx_PyBytes_SingleTailmatch(self, sub, start, end, direction); Py_DECREF(sub); @@ -721,7 +733,7 @@ static CYTHON_INLINE int __Pyx_PyByteArray_AppendObject(PyObject* bytearray, PyO } ival = (unsigned char) (PyString_AS_STRING(value)[0]); } else -#else +#endif #if CYTHON_USE_PYLONG_INTERNALS if (likely(PyLong_CheckExact(value)) && likely(Py_SIZE(value) == 1 || Py_SIZE(value) == 0)) { if (Py_SIZE(value) == 0) { @@ -731,7 +743,6 @@ static CYTHON_INLINE int __Pyx_PyByteArray_AppendObject(PyObject* bytearray, PyO if (unlikely(ival > 255)) goto bad_range; } } else -#endif #endif { // CPython calls PyNumber_Index() internally diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 4cd82291c58702ddef9786b4ab009d0900e05d0b..850f1cbea74aa9531ac43ec513e490d0aede8fcb 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -2,6 +2,9 @@ /* Type Conversion Predeclarations */ +#define __Pyx_uchar_cast(c) ((unsigned char)c) +#define __Pyx_long_cast(x) ((long)x) + #define __Pyx_fits_Py_ssize_t(v, type, is_signed) ( \ (sizeof(type) < sizeof(Py_ssize_t)) || \ (sizeof(type) > sizeof(Py_ssize_t) && \ @@ -13,6 +16,18 @@ (is_signed || likely(v < (type)PY_SSIZE_T_MAX || \ v == (type)PY_SSIZE_T_MAX))) ) +// fast and unsafe abs(Py_ssize_t) that ignores the overflow for (-PY_SSIZE_T_MAX-1) +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + #define __Pyx_sst_abs(value) \ + (sizeof(int) >= sizeof(Py_ssize_t) ? abs(value) : \ + (sizeof(long) >= sizeof(Py_ssize_t) ? labs(value) : llabs(value))) +#else + #define __Pyx_sst_abs(value) \ + (sizeof(int) >= sizeof(Py_ssize_t) ? abs(value) : \ + (sizeof(long) >= sizeof(Py_ssize_t) ? labs(value) : \ + ((value<0) ? -value : value))) +#endif + static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*); static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); @@ -53,8 +68,9 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_Owned_Py_None(b) (Py_INCREF(Py_None), Py_None) -#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) +#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) +#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) +#define __Pyx_PyBool_FromLong(b) ((b) ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False)) static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x); @@ -164,7 +180,7 @@ static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) { } static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT +#if CYTHON_COMPILING_IN_CPYTHON && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT) if ( #if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII __Pyx_sys_getdefaultencoding_not_ascii && @@ -172,7 +188,7 @@ static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_ PyUnicode_Check(o)) { #if PY_VERSION_HEX < 0x03030000 char* defenc_c; - // borrowed, cached reference + // borrowed reference, cached internally in 'o' by CPython PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL); if (!defenc) return NULL; defenc_c = PyBytes_AS_STRING(defenc); @@ -210,7 +226,7 @@ static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_ } else #endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT */ -#if !CYTHON_COMPILING_IN_PYPY +#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) if (PyByteArray_Check(o)) { *length = PyByteArray_GET_SIZE(o); return PyByteArray_AS_STRING(o); @@ -243,7 +259,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { #else if (PyLong_Check(x)) #endif - return Py_INCREF(x), x; + return __Pyx_NewRef(x); m = Py_TYPE(x)->tp_as_number; #if PY_MAJOR_VERSION < 3 if (m && m->nb_int) { @@ -280,22 +296,41 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { return res; } +{{py: from Cython.Utility import pylong_join }} + static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject *x; #if PY_MAJOR_VERSION < 3 - if (likely(PyInt_CheckExact(b))) - return PyInt_AS_LONG(b); + if (likely(PyInt_CheckExact(b))) { + if (sizeof(Py_ssize_t) >= sizeof(long)) + return PyInt_AS_LONG(b); + else + return PyInt_AsSsize_t(x); + } #endif if (likely(PyLong_CheckExact(b))) { - #if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 - #if CYTHON_USE_PYLONG_INTERNALS - switch (Py_SIZE(b)) { - case -1: return -(sdigit)((PyLongObject*)b)->ob_digit[0]; - case 0: return 0; - case 1: return ((PyLongObject*)b)->ob_digit[0]; - } - #endif + #if CYTHON_USE_PYLONG_INTERNALS + const digit* digits = ((PyLongObject*)b)->ob_digit; + const Py_ssize_t size = Py_SIZE(b); + // handle most common case first to avoid indirect branch and optimise branch prediction + if (likely(__Pyx_sst_abs(size) <= 1)) { + ival = likely(size) ? digits[0] : 0; + if (size == -1) ival = -ival; + return ival; + } else { + switch (size) { + {{for _size in (2, 3, 4)}} + {{for _case in (_size, -_size)}} + case {{_case}}: + if (8 * sizeof(Py_ssize_t) > {{_size}} * PyLong_SHIFT) { + return {{'-' if _case < 0 else ''}}(Py_ssize_t) {{pylong_join(_size, 'digits', 'size_t')}}; + } + break; + {{endfor}} + {{endfor}} + } + } #endif return PyLong_AsSsize_t(b); } @@ -347,56 +382,79 @@ static {{struct_type_decl}} {{funcname}}(PyObject * o) { goto bad; } +#if CYTHON_COMPILING_IN_CPYTHON {{for ix, component in enumerate(components):}} {{py:attr = "result.f%s" % ix}} {{attr}} = {{component.from_py_function}}(PyTuple_GET_ITEM(o, {{ix}})); - if ({{component.error_condition(attr)}}) - goto bad; + if ({{component.error_condition(attr)}}) goto bad; {{endfor}} - +#else + { + PyObject *item; + {{for ix, component in enumerate(components):}} + {{py:attr = "result.f%s" % ix}} + item = PySequence_ITEM(o, {{ix}}); if (unlikely(!item)) goto bad; + {{attr}} = {{component.from_py_function}}(item); + Py_DECREF(item); + if ({{component.error_condition(attr)}}) goto bad; + {{endfor}} + } +#endif + return result; bad: return result; } -/////////////// ObjectAsUCS4.proto /////////////// +/////////////// UnicodeAsUCS4.proto /////////////// -static CYTHON_INLINE Py_UCS4 __Pyx_PyObject_AsPy_UCS4(PyObject*); +static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject*); -/////////////// ObjectAsUCS4 /////////////// +/////////////// UnicodeAsUCS4 /////////////// -static CYTHON_INLINE Py_UCS4 __Pyx_PyObject_AsPy_UCS4(PyObject* x) { - long ival; - if (PyUnicode_Check(x)) { - Py_ssize_t length; - #if CYTHON_PEP393_ENABLED - length = PyUnicode_GET_LENGTH(x); - if (likely(length == 1)) { - return PyUnicode_READ_CHAR(x, 0); - } - #else - length = PyUnicode_GET_SIZE(x); - if (likely(length == 1)) { - return PyUnicode_AS_UNICODE(x)[0]; - } - #if Py_UNICODE_SIZE == 2 - else if (PyUnicode_GET_SIZE(x) == 2) { - Py_UCS4 high_val = PyUnicode_AS_UNICODE(x)[0]; - if (high_val >= 0xD800 && high_val <= 0xDBFF) { - Py_UCS4 low_val = PyUnicode_AS_UNICODE(x)[1]; - if (low_val >= 0xDC00 && low_val <= 0xDFFF) { - return 0x10000 + (((high_val & ((1<<10)-1)) << 10) | (low_val & ((1<<10)-1))); - } +static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject* x) { + Py_ssize_t length; + #if CYTHON_PEP393_ENABLED + length = PyUnicode_GET_LENGTH(x); + if (likely(length == 1)) { + return PyUnicode_READ_CHAR(x, 0); + } + #else + length = PyUnicode_GET_SIZE(x); + if (likely(length == 1)) { + return PyUnicode_AS_UNICODE(x)[0]; + } + #if Py_UNICODE_SIZE == 2 + else if (PyUnicode_GET_SIZE(x) == 2) { + Py_UCS4 high_val = PyUnicode_AS_UNICODE(x)[0]; + if (high_val >= 0xD800 && high_val <= 0xDBFF) { + Py_UCS4 low_val = PyUnicode_AS_UNICODE(x)[1]; + if (low_val >= 0xDC00 && low_val <= 0xDFFF) { + return 0x10000 + (((high_val & ((1<<10)-1)) << 10) | (low_val & ((1<<10)-1))); } } - #endif - #endif - PyErr_Format(PyExc_ValueError, - "only single character unicode strings can be converted to Py_UCS4, " - "got length %" CYTHON_FORMAT_SSIZE_T "d", length); - return (Py_UCS4)-1; } + #endif + #endif + PyErr_Format(PyExc_ValueError, + "only single character unicode strings can be converted to Py_UCS4, " + "got length %" CYTHON_FORMAT_SSIZE_T "d", length); + return (Py_UCS4)-1; +} + + +/////////////// ObjectAsUCS4.proto /////////////// +//@requires: UnicodeAsUCS4 + +#define __Pyx_PyObject_AsPy_UCS4(x) \ + (likely(PyUnicode_Check(x)) ? __Pyx_PyUnicode_AsPy_UCS4(x) : __Pyx__PyObject_AsPy_UCS4(x)) +static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject*); + +/////////////// ObjectAsUCS4 /////////////// + +static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject* x) { + long ival; ival = __Pyx_PyInt_As_long(x); if (unlikely(ival < 0)) { if (!PyErr_Occurred()) @@ -411,6 +469,7 @@ static CYTHON_INLINE Py_UCS4 __Pyx_PyObject_AsPy_UCS4(PyObject* x) { return (Py_UCS4)ival; } + /////////////// ObjectAsPyUnicode.proto /////////////// static CYTHON_INLINE Py_UNICODE __Pyx_PyObject_AsPy_UNICODE(PyObject*); @@ -475,14 +534,14 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { return PyInt_FromLong((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { return PyLong_FromUnsignedLong((unsigned long) value); - } else if (sizeof({{TYPE}}) <= sizeof(unsigned long long)) { - return PyLong_FromUnsignedLongLong((unsigned long long) value); + } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); } } else { if (sizeof({{TYPE}}) <= sizeof(long)) { return PyInt_FromLong((long) value); - } else if (sizeof({{TYPE}}) <= sizeof(long long)) { - return PyLong_FromLongLong((long long) value); + } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) { + return PyLong_FromLongLong((PY_LONG_LONG) value); } } { @@ -498,11 +557,19 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { // see CIntFromPy #define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value) \ + __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0) + +#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value) \ + __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1) + +#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc) \ { \ func_type value = func_value; \ if (sizeof(target_type) < sizeof(func_type)) { \ if (unlikely(value != (func_type) (target_type) value)) { \ func_type zero = 0; \ + if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred())) \ + return (target_type) -1; \ if (is_unsigned && unlikely(value < zero)) \ goto raise_neg_overflow; \ else \ @@ -515,10 +582,8 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { /////////////// PyLongInternals /////////////// -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 - #if CYTHON_USE_PYLONG_INTERNALS +#if CYTHON_USE_PYLONG_INTERNALS #include "longintrepr.h" - #endif #endif @@ -530,6 +595,8 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *); //@requires: CIntFromPyVerify //@requires: PyLongInternals +{{py: from Cython.Utility import pylong_join }} + static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = 0; const int is_unsigned = neg_one > const_zero; @@ -548,38 +615,70 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 - #if CYTHON_USE_PYLONG_INTERNALS +#if CYTHON_USE_PYLONG_INTERNALS + const digit* digits = ((PyLongObject*)x)->ob_digit; switch (Py_SIZE(x)) { case 0: return 0; - case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, ((PyLongObject*)x)->ob_digit[0]); + case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, digits[0]) + {{for _size in (2, 3, 4)}} + case {{_size}}: + if (8 * sizeof({{TYPE}}) > {{_size-1}} * PyLong_SHIFT) { + if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT) { + __PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, {{pylong_join(_size, 'digits')}}) + } else if (8 * sizeof({{TYPE}}) >= {{_size}} * PyLong_SHIFT) { + return {{pylong_join(_size, 'digits', TYPE)}}; + } + } + break; + {{endfor}} } - #endif #endif #if CYTHON_COMPILING_IN_CPYTHON if (unlikely(Py_SIZE(x) < 0)) { goto raise_neg_overflow; } +#else + { + // misuse Py_False as a quick way to compare to a '0' int object in PyPy + int result = PyObject_RichCompareBool(x, Py_False, Py_LT); + if (unlikely(result < 0)) + return ({{TYPE}}) -1; + if (unlikely(result == 1)) + goto raise_neg_overflow; + } #endif if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { - __PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, PyLong_AsUnsignedLong(x)) - } else if (sizeof({{TYPE}}) <= sizeof(unsigned long long)) { - __PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long long, PyLong_AsUnsignedLongLong(x)) + __PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, unsigned long, PyLong_AsUnsignedLong(x)) + } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { + __PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) } } else { -#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 - #if CYTHON_USE_PYLONG_INTERNALS + // signed +#if CYTHON_USE_PYLONG_INTERNALS + const digit* digits = ((PyLongObject*)x)->ob_digit; switch (Py_SIZE(x)) { case 0: return 0; - case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +(((PyLongObject*)x)->ob_digit[0])); - case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) ((PyLongObject*)x)->ob_digit[0]); + case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) digits[0]) + case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +digits[0]) + {{for _size in (2, 3, 4)}} + {{for _case in (-_size, _size)}} + case {{_case}}: + if (8 * sizeof({{TYPE}}){{' - 1' if _case < 0 else ''}} > {{_size-1}} * PyLong_SHIFT) { + if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT) { + __PYX_VERIFY_RETURN_INT({{TYPE}}, {{'long' if _case < 0 else 'unsigned long'}}, {{'-(long) ' if _case < 0 else ''}}{{pylong_join(_size, 'digits')}}) + } else if (8 * sizeof({{TYPE}}) - 1 > {{_size}} * PyLong_SHIFT) { + return {{'-' if _case < 0 else ''}}{{pylong_join(_size, 'digits', TYPE)}}; + } + } + break; + {{endfor}} + {{endfor}} } - #endif #endif if (sizeof({{TYPE}}) <= sizeof(long)) { - __PYX_VERIFY_RETURN_INT({{TYPE}}, long, PyLong_AsLong(x)) - } else if (sizeof({{TYPE}}) <= sizeof(long long)) { - __PYX_VERIFY_RETURN_INT({{TYPE}}, long long, PyLong_AsLongLong(x)) + __PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, long, PyLong_AsLong(x)) + } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) { + __PYX_VERIFY_RETURN_INT_EXC({{TYPE}}, PY_LONG_LONG, PyLong_AsLongLong(x)) } } { diff --git a/Cython/Utility/__init__.py b/Cython/Utility/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..40fe94b335acb64e44151f52f062cec1ea17cd5b 100644 --- a/Cython/Utility/__init__.py +++ b/Cython/Utility/__init__.py @@ -0,0 +1,29 @@ + +def pylong_join(count, digits_ptr='digits', join_type='unsigned long'): + """ + Generate an unrolled shift-then-or loop over the first 'count' digits. + Assumes that they fit into 'join_type'. + + (((d[2] << n) | d[1]) << n) | d[0] + """ + return ('(' * (count * 2) + "(%s)" % join_type + ' | '.join( + "%s[%d])%s)" % (digits_ptr, _i, " << PyLong_SHIFT" if _i else '') + for _i in range(count-1, -1, -1))) + + +# although it could potentially make use of data independence, +# this implementation is a bit slower than the simpler one above +def _pylong_join(count, digits_ptr='digits', join_type='unsigned long'): + """ + Generate an or-ed series of shifts for the first 'count' digits. + Assumes that they fit into 'join_type'. + + (d[2] << 2*n) | (d[1] << 1*n) | d[0] + """ + def shift(n): + # avoid compiler warnings for overly large shifts that will be discarded anyway + return " << (%d * PyLong_SHIFT < 8 * sizeof(%s) ? %d * PyLong_SHIFT : 0)" % (n, join_type, n) if n else '' + + return '(%s)' % ' | '.join( + "(((%s)%s[%d])%s)" % (join_type, digits_ptr, i, shift(i)) + for i in range(count-1, -1, -1)) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000000000000000000000000000000000..bc37d2d4b3fa4dd226ef5b921e0a5f3043f2fcbf --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,56 @@ +environment: + + global: + # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the + # /E:ON and /V:ON options are not enabled in the batch script intepreter + # See: http://stackoverflow.com/a/13751649/163740 + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_compiler.cmd" + + matrix: + - PYTHON: "C:\\Python27" + PYTHON_VERSION: "2.7.9" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python33" + PYTHON_VERSION: "3.3.6" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python34" + PYTHON_VERSION: "3.4.3" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python27-x64" + PYTHON_VERSION: "2.7.9" + PYTHON_ARCH: "64" + WINDOWS_SDK_VERSION: "v7.0" + + - PYTHON: "C:\\Python33-x64" + PYTHON_VERSION: "3.3.6" + PYTHON_ARCH: "64" + WINDOWS_SDK_VERSION: "v7.1" + + - PYTHON: "C:\\Python34-x64" + PYTHON_VERSION: "3.4.3" + PYTHON_ARCH: "64" + WINDOWS_SDK_VERSION: "v7.1" + +init: + - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%" + +install: + - "powershell appveyor\\install.ps1" + - "%PYTHON%\\python --version" + +build: off + +test_script: + - "%WITH_COMPILER% %PYTHON%\\python setup.py test" + +after_test: + - "%WITH_COMPILER% %PYTHON%\\python setup.py bdist_wheel" + +artifacts: + - path: dist\* + +#on_success: +# - TODO: upload the content of dist/*.whl to a public wheelhouse diff --git a/appveyor/install.ps1 b/appveyor/install.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..2f96d26b3384608343b01a6211cdedfb2d20857e --- /dev/null +++ b/appveyor/install.ps1 @@ -0,0 +1,85 @@ +# Sample script to install Python and pip under Windows +# Authors: Olivier Grisel and Kyle Kastner +# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ + +$BASE_URL = "https://www.python.org/ftp/python/" +$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" +$GET_PIP_PATH = "C:\get-pip.py" + + +function DownloadPython ($python_version, $platform_suffix) { + $webclient = New-Object System.Net.WebClient + $filename = "python-" + $python_version + $platform_suffix + ".msi" + $url = $BASE_URL + $python_version + "/" + $filename + + $basedir = $pwd.Path + "\" + $filepath = $basedir + $filename + if (Test-Path $filename) { + Write-Host "Reusing" $filepath + return $filepath + } + + # Download and retry up to 5 times in case of network transient errors. + Write-Host "Downloading" $filename "from" $url + $retry_attempts = 3 + for($i=0; $i -lt $retry_attempts; $i++){ + try { + $webclient.DownloadFile($url, $filepath) + break + } + Catch [Exception]{ + Start-Sleep 1 + } + } + Write-Host "File saved at" $filepath + return $filepath +} + + +function InstallPython ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "" + } else { + $platform_suffix = ".amd64" + } + $filepath = DownloadPython $python_version $platform_suffix + Write-Host "Installing" $filepath "to" $python_home + $args = "/qn /i $filepath TARGETDIR=$python_home" + Write-Host "msiexec.exe" $args + Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru + Write-Host "Python $python_version ($architecture) installation complete" + return $true +} + + +function InstallPip ($python_home) { + $pip_path = $python_home + "\Scripts\pip.exe" + $python_path = $python_home + "\python.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) + Write-Host "Executing:" $python_path $GET_PIP_PATH + Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru + } else { + Write-Host "pip already installed." + } +} + +function InstallPackage ($python_home, $pkg) { + $pip_path = $python_home + "\Scripts\pip.exe" + & $pip_path install $pkg +} + +function main () { + InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON + InstallPip $env:PYTHON + InstallPackage $env:PYTHON wheel +} + +main diff --git a/appveyor/run_with_compiler.cmd b/appveyor/run_with_compiler.cmd new file mode 100644 index 0000000000000000000000000000000000000000..3a472bc836c30415445def35bc58338a1cbe38c5 --- /dev/null +++ b/appveyor/run_with_compiler.cmd @@ -0,0 +1,47 @@ +:: To build extensions for 64 bit Python 3, we need to configure environment +:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: +:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) +:: +:: To build extensions for 64 bit Python 2, we need to configure environment +:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: +:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) +:: +:: 32 bit builds do not require specific environment configurations. +:: +:: Note: this script needs to be run with the /E:ON and /V:ON flags for the +:: cmd interpreter, at least for (SDK v7.0) +:: +:: More details at: +:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows +:: http://stackoverflow.com/a/13751649/163740 +:: +:: Author: Olivier Grisel +:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ +@ECHO OFF + +SET COMMAND_TO_RUN=%* +SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows + +SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" +IF %MAJOR_PYTHON_VERSION% == "2" ( + SET WINDOWS_SDK_VERSION="v7.0" +) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( + SET WINDOWS_SDK_VERSION="v7.1" +) ELSE ( + ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" + EXIT 1 +) + +IF "%PYTHON_ARCH%"=="64" ( + ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture + SET DISTUTILS_USE_SDK=1 + SET MSSdk=1 + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 +) ELSE ( + ECHO Using default MSVC build environment for 32 bit architecture + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 +) diff --git a/docs/src/quickstart/build.rst b/docs/src/quickstart/build.rst index 78ee142155a04a356e5f235b1200cbe482daaef8..41abf280009d8bf9c959b6ba2b16a3ab8814400c 100644 --- a/docs/src/quickstart/build.rst +++ b/docs/src/quickstart/build.rst @@ -60,9 +60,9 @@ use pip: (venv)$ ipython notebook To enable support for Cython compilation, install Cython and load the -``cythonmagic`` extension from within IPython:: +``Cython`` extension from within IPython:: - %load_ext cythonmagic + %load_ext Cython Then, prefix a cell with the ``%%cython`` marker to compile it:: diff --git a/docs/src/tutorial/memory_allocation.rst b/docs/src/tutorial/memory_allocation.rst index 8d82e36f53bf49562fc54026f6e84211ab248840..eb0d45fadfbd1d58fe556090650fc4d976a21083 100644 --- a/docs/src/tutorial/memory_allocation.rst +++ b/docs/src/tutorial/memory_allocation.rst @@ -22,7 +22,7 @@ management in C. Simple C values and structs (such as a local variable ``cdef double x``) are usually allocated on the stack and passed by value, but for larger and more complicated objects (e.g. a dynamically-sized list of doubles), the memory must -be manually requested and released. C provides the functions :c:func`malloc`, +be manually requested and released. C provides the functions :c:func:`malloc`, :c:func:`realloc`, and :c:func:`free` for this purpose, which can be imported in cython from ``clibc.stdlib``. Their signatures are: diff --git a/docs/src/tutorial/numpy.rst b/docs/src/tutorial/numpy.rst index 54ea330972eac745aecb69c2e05ed84edc14b759..fa0cd2ab8b556a46f2cc1eb5e73862e2aacd9b6f 100644 --- a/docs/src/tutorial/numpy.rst +++ b/docs/src/tutorial/numpy.rst @@ -234,7 +234,7 @@ The array lookups are still slowed down by two factors: Now bounds checking is not performed (and, as a side-effect, if you ''do'' happen to access out of bounds you will in the best case crash your program and in the worst case corrupt data). It is possible to switch bounds-checking -mode in many ways, see [:reference/directives:compiler directives] for more +mode in many ways, see :ref:`compiler-directives` for more information. Negative indices are dealt with by ensuring Cython that the indices will be diff --git a/docs/src/userguide/pypy.rst b/docs/src/userguide/pypy.rst index 6d1dd5b1f34cdeb66fd3278b9fdb26fd4bc71996..e0fd09c549e38e8f404ca1b3eff33ff43d9c5b72 100644 --- a/docs/src/userguide/pypy.rst +++ b/docs/src/userguide/pypy.rst @@ -1,8 +1,8 @@ Porting Cython code to PyPy -============================ +=========================== Since version 0.17, Cython has basic support for cpyext, the layer in -`PyPy <http://pypy.org>`_ that emulates CPython's C-API. This is +`PyPy <http://pypy.org/>`_ that emulates CPython's C-API. This is achieved by making the generated C code adapt at C compile time, so the generated code will compile in both CPython and PyPy unchanged. @@ -14,7 +14,7 @@ that works in both CPython and PyPy. Reference counts ------------------ +---------------- A general design difference in PyPy is that the runtime does not use reference counting internally but always a garbage collector. Reference @@ -25,7 +25,7 @@ any references held in Python space. Object lifetime ----------------- +--------------- As a direct consequence of the different garbage collection characteristics, objects may see the end of their lifetime at other points than in @@ -45,7 +45,7 @@ e.g. when context managers can be used together with the ``with`` statement. Borrowed references and data pointers --------------------------------------- +------------------------------------- The memory management in PyPy is allowed to move objects around in memory. The C-API layer is only an indirect view on PyPy objects and often replicates @@ -88,7 +88,7 @@ when done with it to convert it into an owned reference. Builtin types, slots and fields --------------------------------- +------------------------------- The following builtin types are not currently available in cpyext in form of their C level representation: :c:type:`PyComplexObject`, @@ -110,8 +110,19 @@ protocols for object operations. Cython will map them to an appropriate usage of the C-API in both CPython and cpyext. +GIL handling +------------ + +Currently, the GIL handling function :c:func:`PyGILState_Ensure` is not +re-entrant in PyPy and deadlocks when called twice. This means that +code that tries to acquire the GIL "just in case", because it might be +called with or without the GIL, will not work as expected in PyPy. +See `PyGILState_Ensure should not deadlock if GIL already held +<https://bitbucket.org/pypy/pypy/issue/1778>`_. + + Efficiency ------------ +---------- Simple functions and especially macros that are used for speed in CPython may exhibit substantially different performance characteristics in cpyext. @@ -137,16 +148,19 @@ than Cython currently does, it's best to fix Cython for everyone's benefit. Known problems ---------------- +-------------- * As of PyPy 1.9, subtyping builtin types can result in infinite recursion on method calls in some rare cases. * Docstrings of special methods are not propagated to Python space. +* The Python 3.x adaptations in pypy3 only slowly start to include the + C-API, so more incompatibilities can be expected there. + Bugs and crashes ------------------ +---------------- The cpyext implementation in PyPy is much younger and substantially less mature than the well tested C-API and its underlying native implementation diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index 1ec52e1170f9bad06b9bb4999539705629ca8f5b..6a0486a2be8fc183f0475fca6ec79e9a145c9994 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -68,7 +68,7 @@ explicitly, you can use glob patterns:: You can also use glob patterns in :class:`Extension` objects if you pass them through :func:`cythonize`:: - extensions = [Extension("*", "*.pyx")] + extensions = [Extension("*", ["*.pyx"])] setup( ext_modules = cythonize(extensions) diff --git a/runtests.py b/runtests.py index a0f0c92ab6abe92a53bdaeb13e10b520610a4755..e4b7b586f024b0c58206539e69486d8d24f72536 100755 --- a/runtests.py +++ b/runtests.py @@ -98,14 +98,15 @@ def get_distutils_distro(_cache=[]): EXT_DEP_MODULES = { - 'tag:numpy' : 'numpy', - 'tag:pstats': 'pstats', - 'tag:posix' : 'posix', - 'tag:array' : 'array', + 'tag:numpy': 'numpy', + 'tag:asyncio': 'asyncio', + 'tag:pstats': 'pstats', + 'tag:posix': 'posix', + 'tag:array': 'array', 'tag:coverage': 'Cython.Coverage', - 'Coverage': 'Cython.Coverage', - 'tag:ipython': 'IPython', - 'tag:jedi': 'jedi', + 'Coverage': 'Cython.Coverage', + 'tag:ipython': 'IPython', + 'tag:jedi': 'jedi', } def patch_inspect_isfunction(): @@ -271,6 +272,10 @@ def get_openmp_compiler_flags(language): if not gcc_version: return None # not gcc - FIXME: do something about other compilers + # gcc defines "__int128_t", assume that at least all 64 bit architectures have it + global COMPILER_HAS_INT128 + COMPILER_HAS_INT128 = getattr(sys, 'maxsize', getattr(sys, 'maxint', 0)) > 2**60 + compiler_version = gcc_version.group(1) if compiler_version and compiler_version.split('.') >= ['4', '2']: return '-fopenmp', '-fopenmp' @@ -280,6 +285,8 @@ try: except locale.Error: pass +COMPILER = None +COMPILER_HAS_INT128 = False OPENMP_C_COMPILER_FLAGS = get_openmp_compiler_flags('c') OPENMP_CPP_COMPILER_FLAGS = get_openmp_compiler_flags('cpp') @@ -322,12 +329,12 @@ VER_DEP_MODULES = { (3,1): (_is_py3_before_32, lambda x: x in ['run.pyclass_special_methods', ]), (3,3) : (operator.lt, lambda x: x in ['build.package_compilation', + 'run.yield_from_py33', ]), - (3,4,0,'beta',3) : (operator.le, lambda x: x in ['run.py34_signature', - ]), + (3,4): (operator.lt, lambda x: x in ['run.py34_signature', + ]), } -COMPILER = None INCLUDE_DIRS = [ d for d in os.getenv('INCLUDE', '').split(os.pathsep) if d ] CFLAGS = os.getenv('CFLAGS', '').split() CCACHE = os.getenv('CYTHON_RUNTESTS_CCACHE', '').split() @@ -1693,7 +1700,7 @@ def main(): action="store_true", default=False, help="only compile pyx to c, do not run C compiler or run the tests") parser.add_option("--no-refnanny", dest="with_refnanny", - action="store_false", default=not IS_PYPY, + action="store_false", default=True, help="do not regression test reference counting") parser.add_option("--no-fork", dest="fork", action="store_false", default=True, @@ -1933,7 +1940,7 @@ def runtests(options, cmd_args, coverage=None): try: import jedi - if list(map(int, re.findall('[0-9]+', jedi.__version__ or '0'))) < [0, 8, 1]: + if not ([0, 8, 1] <= list(map(int, re.findall('[0-9]+', jedi.__version__ or '0'))) < [0, 9]): raise ImportError except (ImportError, AttributeError, TypeError): exclude_selectors.append(RegExSelector('Jedi')) @@ -1941,11 +1948,17 @@ def runtests(options, cmd_args, coverage=None): if options.exclude: exclude_selectors += [ string_selector(r) for r in options.exclude ] + if not COMPILER_HAS_INT128 or not IS_CPYTHON: + exclude_selectors += [RegExSelector('int128')] + if options.shard_num > -1: exclude_selectors.append(ShardExcludeSelector(options.shard_num, options.shard_count)) if not test_bugs: - exclude_selectors += [ FileListExcluder(os.path.join(ROOTDIR, "bugs.txt"), verbose=verbose_excludes) ] + exclude_selectors += [ + FileListExcluder(os.path.join(ROOTDIR, bugs_file_name), verbose=verbose_excludes) + for bugs_file_name in ['bugs.txt'] + (['pypy_bugs.txt'] if IS_PYPY else []) + ] if sys.platform in ['win32', 'cygwin'] and sys.version_info < (2,6): exclude_selectors += [ lambda x: x == "run.specialfloat" ] diff --git a/setup.py b/setup.py index c9e6f1a1c8287f237e5c344885e9c6a331a2a23a..b5698f6a78ddfd6069c61c518c5b19bbb0b4d6d2 100755 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ except ImportError: import os import stat import subprocess +import textwrap import sys import platform @@ -313,48 +314,45 @@ if include_debugger: setup_args['package_data']['Cython.Debugger.Tests'] = ['codefile', 'cfuncs.c'] setup( - name = 'Cython', - version = version, - url = 'http://www.cython.org', - author = 'Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.', - author_email = 'cython-devel@python.org', - description = "The Cython compiler for writing C extensions for the Python language.", - long_description = """\ - The Cython language makes writing C extensions for the Python language as - easy as Python itself. Cython is a source code translator based on the - well-known Pyrex_, but supports more cutting edge functionality and - optimizations. - - The Cython language is very close to the Python language (and most Python - code is also valid Cython code), but Cython additionally supports calling C - functions and declaring C types on variables and class attributes. This - allows the compiler to generate very efficient C code from Cython code. - - This makes Cython the ideal language for writing glue code for external C - libraries, and for fast C modules that speed up the execution of Python - code. - - .. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ - """, - classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - "Programming Language :: C", - "Programming Language :: Cython", - "Topic :: Software Development :: Code Generators", - "Topic :: Software Development :: Compilers", - "Topic :: Software Development :: Libraries :: Python Modules" - ], - - scripts = scripts, - packages=packages, - - py_modules = ["cython"], - - **setup_args - ) + name='Cython', + version=version, + url='http://www.cython.org', + author='Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.', + author_email='cython-devel@python.org', + description="The Cython compiler for writing C extensions for the Python language.", + long_description=textwrap.dedent("""\ + The Cython language makes writing C extensions for the Python language as + easy as Python itself. Cython is a source code translator based on Pyrex_, + but supports more cutting edge functionality and optimizations. + + The Cython language is very close to the Python language (and most Python + code is also valid Cython code), but Cython additionally supports calling C + functions and declaring C types on variables and class attributes. This + allows the compiler to generate very efficient C code from Cython code. + + This makes Cython the ideal language for writing glue code for external C + libraries, and for fast C modules that speed up the execution of Python + code. + + .. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ + """), + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Programming Language :: C", + "Programming Language :: Cython", + "Topic :: Software Development :: Code Generators", + "Topic :: Software Development :: Compilers", + "Topic :: Software Development :: Libraries :: Python Modules" + ], + + scripts=scripts, + packages=packages, + py_modules=["cython"], + **setup_args +) diff --git a/tests/bugs.txt b/tests/bugs.txt index 5235bc933725db2b6f7b7aeebc5877a52263c83b..62c5e5d89b56c0b9a4376e1762ed8ce9b7f5f392 100644 --- a/tests/bugs.txt +++ b/tests/bugs.txt @@ -44,6 +44,8 @@ pyregr.test_exceptions pyregr.test_subprocess pyregr.test_zipfile64 pyregr.test_tuple +pyregr.test_urllib2net +pyregr.test_urllibnet # Inlined generators all diff --git a/tests/build/common_include_dir.srctree b/tests/build/common_include_dir.srctree index a03bac28a9c351329ae5aa8b865537764a838bc6..8867c46210708bb8c521606f6e2f8eecc2c709e8 100644 --- a/tests/build/common_include_dir.srctree +++ b/tests/build/common_include_dir.srctree @@ -14,18 +14,21 @@ PYTHON fake_grep.py -c '#include "common/AddTraceback_impl_.*h"' c.c ######## setup.py ######## +import sys from Cython.Build.Dependencies import cythonize from distutils.core import setup # Test concurrent safety if multiprocessing is available. # (In particular, TravisCI does not support spawning processes from tests.) -try: - import multiprocessing - multiprocessing.Pool(2).close() - nthreads = 2 -except (ImportError, OSError): - nthreads = 0 +nthreads = 0 +if not hasattr(sys, 'pypy_version_info'): + try: + import multiprocessing + multiprocessing.Pool(2).close() + nthreads = 2 + except (ImportError, OSError): + pass setup( ext_modules = cythonize("*.pyx", common_utility_include_dir='common', nthreads=nthreads), diff --git a/tests/compile/distutils_libraries_T845.srctree b/tests/compile/distutils_libraries_T845.srctree new file mode 100644 index 0000000000000000000000000000000000000000..69c85492c705608bfb478ccf1c81889c5e334c5e --- /dev/null +++ b/tests/compile/distutils_libraries_T845.srctree @@ -0,0 +1,33 @@ +PYTHON setup.py build_ext --inplace + +######## setup.py ######## + +from Cython.Build import cythonize +from Cython.Distutils.extension import Extension + +ext_modules = [ + Extension("a", ["a.pyx"]), + Extension("x", ["x.pyx"]), + Extension("y", ["y.pyx"]), +] + +ext_modules = cythonize(ext_modules) + +assert ext_modules[1].libraries == ["lib_x"] +assert ext_modules[2].libraries == ["lib_y"] + +######## libx.pxd ######## +# distutils: libraries = lib_x + +######## liby.pxd ######## +# distutils: libraries = lib_y + +######## a.pyx ######## +cimport libx +cimport liby + +######## x.pyx ######## +cimport libx + +######## y.pyx ######## +cimport liby diff --git a/tests/errors/callargs.pyx b/tests/errors/callargs.pyx index 73db3a9776653566a1cac8512f5bdd28a15cb3b2..edcd702b7b43621831c92757444d0957602adb26 100644 --- a/tests/errors/callargs.pyx +++ b/tests/errors/callargs.pyx @@ -7,16 +7,21 @@ args = (1,2,3) kwargs = {u"test" : "toast"} def test(): + # ok + f(1, 2, c=3, *args, d=5, **kwargs, **kwargs) + f(1, 2, c=3, *args, d=5, **kwargs, x=6) + f(1, 2, **kwargs, c=3) + f(1, 2, c=3, *args, *args, **kwargs) f(*args, 1, 2, 3) + # errors f(**kwargs, 1, 2, c=3) f(*args, **kwargs, *args) f(1, 2, c=3, *args, **kwargs, *args) - f(1, 2, c=3, *args, d=5, **kwargs, **kwargs) - f(1, 2, c=3, *args, d=5, **kwargs, x=6) f(1=2) + # too bad we don't get more errors here ... _ERRORS = u""" -10:13: Non-keyword arg following star-arg +17:16: Non-keyword arg following keyword arg """ diff --git a/tests/errors/e_relative_cimport.pyx b/tests/errors/e_relative_cimport.pyx index b15ab4801a742a17e39a70e64cb7694a12c55926..36a134411e133b3ac35252573b8e9840539c9468 100644 --- a/tests/errors/e_relative_cimport.pyx +++ b/tests/errors/e_relative_cimport.pyx @@ -2,16 +2,14 @@ # tag: cimport from ..relative_cimport cimport some_name - +from .e_relative_cimport cimport some_name from ..cython cimport declare +from . cimport e_relative_cimport _ERRORS=""" -4:0: 'relative_cimport.pxd' not found -4:0: 'some_name.pxd' not found 4:0: relative cimport beyond main package is not allowed -4:32: Name 'some_name' not declared in module 'relative_cimport' -6:0: 'declare.pxd' not found +5:0: relative cimport beyond main package is not allowed 6:0: relative cimport beyond main package is not allowed -6:22: Name 'declare' not declared in module 'cython' +7:0: relative cimport beyond main package is not allowed """ diff --git a/tests/errors/extended_unpacking.pyx b/tests/errors/extended_unpacking.pyx index eeae75670786fa7c42d485d62fb3fd2b35b8397e..c5b5f6145b32bbcf6549b2ebf0a7fac858125bf6 100644 --- a/tests/errors/extended_unpacking.pyx +++ b/tests/errors/extended_unpacking.pyx @@ -16,6 +16,8 @@ def syntax1(): [*a, *b] (a, b, *c, d, e, f, *g, h, i) + [a, b, *c, d, e, f, *g, h, i] + {a, b, *c, d, e, f, *g, h, i} def syntax2(): @@ -33,19 +35,15 @@ def types(l): _ERRORS = u""" # syntax1() - 8: 4: can use starred expression only as assignment target -10: 4: can use starred expression only as assignment target -12: 4: can use starred expression only as assignment target -14: 4: can use starred expression only as assignment target -16: 5: can use starred expression only as assignment target -16: 9: can use starred expression only as assignment target -18:11: can use starred expression only as assignment target -18:24: can use starred expression only as assignment target + 8: 4: starred expression is not allowed here +10: 4: starred expression is not allowed here +12: 4: starred expression is not allowed here +14: 4: starred expression is not allowed here # syntax2() -24:11: more than 1 starred expression in assignment +26:11: more than 1 starred expression in assignment # types() -30:15: Cannot coerce list to type 'int' -31:10: starred target must have Python object (list) type +32:15: Cannot coerce list to type 'int' +33:10: starred target must have Python object (list) type """ diff --git a/tests/errors/nogil.pyx b/tests/errors/nogil.pyx index 98b694049585f9c6a499aaf4a3c0994de1890429..9ccea843863d87f33d4d88f3be29b9f14bbe23e6 100644 --- a/tests/errors/nogil.pyx +++ b/tests/errors/nogil.pyx @@ -42,6 +42,7 @@ cdef object m(): (x, y) [x, y] {x: y} + {x, y} obj and x t(obj) # f(42) # Cython handles this internally @@ -129,35 +130,37 @@ _ERRORS = u""" 42:9: Discarding owned Python object not allowed without gil 43:8: Constructing Python list not allowed without gil 43:8: Discarding owned Python object not allowed without gil -44:8: Constructing Python dict not allowed without gil -44:8: Discarding owned Python object not allowed without gil -45:12: Discarding owned Python object not allowed without gil -45:12: Truth-testing Python object not allowed without gil -46:13: Python type test not allowed without gil -48:10: Discarding owned Python object not allowed without gil -48:10: Operation not allowed without gil -49:8: Discarding owned Python object not allowed without gil -49:8: Operation not allowed without gil -50:10: Assignment of Python object not allowed without gil -50:14: Assignment of Python object not allowed without gil -51:9: Assignment of Python object not allowed without gil -51:13: Assignment of Python object not allowed without gil -51:16: Creating temporary Python reference not allowed without gil -51:19: Creating temporary Python reference not allowed without gil -52:11: Assignment of Python object not allowed without gil -52:11: Indexing Python object not allowed without gil -53:11: Accessing Python attribute not allowed without gil +44:10: Constructing Python dict not allowed without gil +44:10: Discarding owned Python object not allowed without gil +45:10: Constructing Python set not allowed without gil +45:10: Discarding owned Python object not allowed without gil +46:12: Discarding owned Python object not allowed without gil +46:12: Truth-testing Python object not allowed without gil +47:13: Python type test not allowed without gil +49:10: Discarding owned Python object not allowed without gil +49:10: Operation not allowed without gil +50:8: Discarding owned Python object not allowed without gil +50:8: Operation not allowed without gil +51:10: Assignment of Python object not allowed without gil +51:14: Assignment of Python object not allowed without gil +52:9: Assignment of Python object not allowed without gil +52:13: Assignment of Python object not allowed without gil +52:16: Creating temporary Python reference not allowed without gil +52:19: Creating temporary Python reference not allowed without gil 53:11: Assignment of Python object not allowed without gil -54:8: Constructing Python tuple not allowed without gil -54:8: Python print statement not allowed without gil -55:8: Deleting Python object not allowed without gil -56:8: Returning Python object not allowed without gil -57:8: Raising exception not allowed without gil -58:14: Truth-testing Python object not allowed without gil -60:17: Truth-testing Python object not allowed without gil -62:8: For-loop using object bounds or target not allowed without gil -62:14: Coercion from Python not allowed without the GIL -62:25: Coercion from Python not allowed without the GIL -64:8: Try-except statement not allowed without gil -85:8: For-loop using object bounds or target not allowed without gil +53:11: Indexing Python object not allowed without gil +54:11: Accessing Python attribute not allowed without gil +54:11: Assignment of Python object not allowed without gil +55:8: Constructing Python tuple not allowed without gil +55:8: Python print statement not allowed without gil +56:8: Deleting Python object not allowed without gil +57:8: Returning Python object not allowed without gil +58:8: Raising exception not allowed without gil +59:14: Truth-testing Python object not allowed without gil +61:17: Truth-testing Python object not allowed without gil +63:8: For-loop using object bounds or target not allowed without gil +63:14: Coercion from Python not allowed without the GIL +63:25: Coercion from Python not allowed without the GIL +65:8: Try-except statement not allowed without gil +86:8: For-loop using object bounds or target not allowed without gil """ diff --git a/tests/errors/pep448_syntax_1.pyx b/tests/errors/pep448_syntax_1.pyx new file mode 100644 index 0000000000000000000000000000000000000000..ca262971462ca2f08ef5858194f794d31baff84d --- /dev/null +++ b/tests/errors/pep448_syntax_1.pyx @@ -0,0 +1,10 @@ +# mode: error +# tag: pep448 + +def unpack_mix(): + [*1, **1] + + +_ERRORS = """ +5:9: Expected an identifier or literal +""" diff --git a/tests/errors/pep448_syntax_2.pyx b/tests/errors/pep448_syntax_2.pyx new file mode 100644 index 0000000000000000000000000000000000000000..45027af931b3b82ffbc14ae60f64bdd48319010d --- /dev/null +++ b/tests/errors/pep448_syntax_2.pyx @@ -0,0 +1,10 @@ +# mode: error +# tag: pep448 + +def unpack_wrong_stars(): + [**1] + + +_ERRORS = """ +5:5: Expected an identifier or literal +""" diff --git a/tests/errors/pep448_syntax_3.pyx b/tests/errors/pep448_syntax_3.pyx new file mode 100644 index 0000000000000000000000000000000000000000..e81119e32e6a9b9c59956a47df7fbe5553d45421 --- /dev/null +++ b/tests/errors/pep448_syntax_3.pyx @@ -0,0 +1,10 @@ +# mode: error +# tag: pep448 + +def unpack_mix_in_set(): + {*1, **2} + + +_ERRORS = """ +5:9: unexpected **item found in set literal +""" diff --git a/tests/pypy_bugs.txt b/tests/pypy_bugs.txt new file mode 100644 index 0000000000000000000000000000000000000000..4651cc0bf0c77ad0237a9cf58feea3c2b15658e2 --- /dev/null +++ b/tests/pypy_bugs.txt @@ -0,0 +1,43 @@ +# This file contains tests corresponding to unresolved bugs +# either in PyPy, PyPy's cpyext, or Cython under PyPy, +# which will be skipped in the normal testing run. + +broken_exception +bufaccess +exarkun +memoryview +memslice +sequential_parallel +setjmp +yield_from_pep380 +memoryview_inplace_division + +# GIL issues +# https://bitbucket.org/pypy/pypy/issue/2025/cpyext-finish-implementation-of +run.exceptions_nogil +run.nogil +run.with_gil +run.parallel + +# gc issue? +memoryview_in_subclasses +external_ref_reassignment +run.exttype_dealloc + +# https://bitbucket.org/pypy/pypy/issue/2023/cpyext-pydict_keys-values-items-does-not +builtin_subtype_methods_cy3 + +# bugs in cpyext +run.special_methods_T561 +run.special_methods_T561_py2 + +# tests for things that don't exist in cpyext +compile.pylong +run.datetime_pxd +run.datetime_cimport +run.datetime_members +run.extern_builtins_T258 +run.unicode_ascii_auto_encoding +run.unicode_default_auto_encoding +run.str_ascii_auto_encoding +run.str_default_auto_encoding diff --git a/tests/run/addop.pyx b/tests/run/addop.pyx index e283043470f47c38b653bea8c879efb3b793759a..c8d474be1bdbf98f25015f195611b11314fc1d33 100644 --- a/tests/run/addop.pyx +++ b/tests/run/addop.pyx @@ -1,6 +1,10 @@ cimport cython +def bigint(x): + print(str(x).rstrip('L')) + + def mixed_test(): """ >>> mixed_test() @@ -29,6 +33,10 @@ def add_x_1(x): 2 >>> add_x_1(-1) 0 + >>> bigint(2**50 + 1) + 1125899906842625 + >>> bigint(add_x_1(2**50)) + 1125899906842625 >>> add_x_1(1.5) 2.5 >>> add_x_1(-1.5) @@ -73,13 +81,17 @@ def add_x_large(x): -1073741824.0 >>> add_x_large(2**30 + 1) 2147483649 + >>> bigint(2**50 + 1 + 2**30) + 1125900980584449 + >>> bigint(add_x_large(2**50 + 1)) + 1125900980584449 >>> 2**31 + 2**30 3221225472 >>> add_x_large(2**31) 3221225472 - >>> print(2**66 + 2**30) + >>> bigint(2**66 + 2**30) 73786976295911948288 - >>> print(add_x_large(2**66)) + >>> bigint(add_x_large(2**66)) 73786976295911948288 >>> try: add_x_large("abc") ... except TypeError: pass @@ -96,6 +108,10 @@ def add_1_x(x): 2 >>> add_1_x(-1) 0 + >>> bigint(2**50 + 1) + 1125899906842625 + >>> bigint(add_1_x(2**50)) + 1125899906842625 >>> add_1_x(1.5) 2.5 >>> add_1_x(-1.5) @@ -115,6 +131,12 @@ def add_1f_x(x): 2.0 >>> add_1f_x(-1) 0.0 + >>> 1.0 + 2**52 + 4503599627370497.0 + >>> add_1f_x(2**52) + 4503599627370497.0 + >>> add_1f_x(2**60) == 1.0 + 2**60 or add_1f_x(2**60) + True >>> add_1f_x(1.5) 2.5 >>> add_1f_x(-1.5) diff --git a/tests/run/asyncio_generators.srctree b/tests/run/asyncio_generators.srctree new file mode 100644 index 0000000000000000000000000000000000000000..d8d7215353ad300a8d09b412471dfcd2593755cb --- /dev/null +++ b/tests/run/asyncio_generators.srctree @@ -0,0 +1,103 @@ +# mode: run +# tag: asyncio + +""" +PYTHON setup.py build_ext -i +PYTHON test_from_import.py +PYTHON test_import.py +PYTHON test_both.py +""" + +######## setup.py ######## + +from Cython.Build.Dependencies import cythonize +from distutils.core import setup + +setup( + ext_modules = cythonize("*.pyx"), +) + + +######## test_from_import.py ######## + +import from_asyncio_import +import asyncio + +def runloop(task): + loop = asyncio.get_event_loop() + result = loop.run_until_complete(task()) + assert 3 == result, result + +runloop(from_asyncio_import.wait3) + + +######## test_import.py ######## + +import import_asyncio +import asyncio + +def runloop(task): + loop = asyncio.get_event_loop() + result = loop.run_until_complete(task()) + assert 3 == result, result + +runloop(import_asyncio.wait3) + + +######## test_both.py ######## + +import asyncio + +def runloop(task): + loop = asyncio.get_event_loop() + result = loop.run_until_complete(task()) + assert 3 == result, result + +import import_asyncio +runloop(import_asyncio.wait3) # 1a) +import from_asyncio_import +runloop(from_asyncio_import.wait3) # 1b) + +runloop(from_asyncio_import.wait3) # 2a) +runloop(import_asyncio.wait3) # 2b) +runloop(from_asyncio_import.wait3) # 3a) +runloop(import_asyncio.wait3) # 3b) + +try: + from collections.abc import Generator +except ImportError: + from collections import Generator + +assert isinstance(from_asyncio_import.wait3(), Generator) +assert isinstance(import_asyncio.wait3(), Generator) +assert isinstance((lambda:(yield))(), Generator) + + +######## import_asyncio.pyx ######## +# cython: binding=True + +import asyncio + +@asyncio.coroutine +def wait3(): + counter = 0 + for i in range(3): + print(counter) + yield from asyncio.sleep(0.01) + counter += 1 + return counter + + +######## from_asyncio_import.pyx ######## +# cython: binding=True + +from asyncio import coroutine, sleep + +@coroutine +def wait3(): + counter = 0 + for i in range(3): + print(counter) + yield from sleep(0.01) + counter += 1 + return counter diff --git a/tests/run/builtin_ord.pyx b/tests/run/builtin_ord.pyx index f9bf0bb7697c50d4e97f11ad706e012da384d26c..84eb1021167ab629e7c058af3c7091602f91230e 100644 --- a/tests/run/builtin_ord.pyx +++ b/tests/run/builtin_ord.pyx @@ -1,11 +1,18 @@ cimport cython +import sys + uspace = u' ' ustring_with_a = u'abcdefg' ustring_without_a = u'bcdefg' -@cython.test_fail_if_path_exists('//SimpleCallNode') + +@cython.test_assert_path_exists( + # ord() should receive and return a C value + '//ReturnStatNode//CoerceToPyTypeNode//SimpleCallNode') +@cython.test_fail_if_path_exists( + '//ReturnStatNode//SimpleCallNode//CoerceToPyTypeNode') def ord_Py_UNICODE(unicode s): """ >>> ord_Py_UNICODE(uspace) @@ -15,17 +22,23 @@ def ord_Py_UNICODE(unicode s): u = s[0] return ord(u) -@cython.test_assert_path_exists('//IntNode') + +@cython.test_assert_path_exists('//TupleNode//IntNode') @cython.test_fail_if_path_exists('//SimpleCallNode') def ord_const(): """ - >>> ord_const() + >>> ord(b' ') + 32 + >>> ord(' ') 32 + >>> ord_const() + (32, 32, 32, 255, 255, 4660, 0) """ - return ord(u' ') + return ord(u' '), ord(b' '), ord(' '), ord('\xff'), ord(b'\xff'), ord(u'\u1234'), ord('\0') + @cython.test_assert_path_exists('//PrimaryCmpNode//IntNode') -@cython.test_fail_if_path_exists('//SimpleCallNode') +#@cython.test_fail_if_path_exists('//SimpleCallNode') def unicode_for_loop_ord(unicode s): """ >>> unicode_for_loop_ord(ustring_with_a) @@ -37,3 +50,43 @@ def unicode_for_loop_ord(unicode s): if ord(c) == ord(u'a'): return True return False + + +def compare_to_char(s): + """ + >>> compare_to_char(uspace) + False + >>> compare_to_char(b'a') + False + >>> compare_to_char(b'x') + True + >>> compare_to_char('x') + True + """ + cdef char c = b'x' + return ord(s) == c + + +def ord_object(s): + """ + >>> try: ord_object('abc') + ... except ValueError: assert sys.version_info[0] >= 3 + ... except TypeError: assert sys.version_info[0] < 3 + >>> ord_object('a') + 97 + >>> ord_object(b'a') + 97 + """ + return ord(s) + + +def non_builtin_ord(s): + """ + >>> non_builtin_ord('x') + (123, 123) + """ + def _ord(s): + return 123 + + ord = _ord + return ord(s), _ord(s) diff --git a/tests/run/builtins_truth_test.pyx b/tests/run/builtins_truth_test.pyx index be20d64c9f5fc16791ea9c226978ceb4c5f3e7b8..cad7162f6b72cf82fd61b8a46202f46ee2a16d32 100644 --- a/tests/run/builtins_truth_test.pyx +++ b/tests/run/builtins_truth_test.pyx @@ -10,6 +10,7 @@ def bool_list(list obj): """ return bool(obj) + def if_list(list obj): """ >>> if_list( [] ) @@ -24,6 +25,7 @@ def if_list(list obj): else: return False + def if_list_nogil(list obj): """ >>> if_list_nogil( [] ) @@ -41,6 +43,7 @@ def if_list_nogil(list obj): result = False return result + def if_list_literal(t): """ >>> if_list_literal(True) @@ -71,6 +74,7 @@ def bool_tuple(tuple obj): """ return bool(obj) + def if_tuple(tuple obj): """ >>> if_tuple( () ) @@ -85,6 +89,7 @@ def if_tuple(tuple obj): else: return False + def if_tuple_literal(t): """ >>> if_tuple_literal(True) @@ -104,6 +109,97 @@ def if_tuple_literal(t): return False +def bool_set(set obj): + """ + >>> bool_set( set() ) + False + >>> bool_set( set([1]) ) + True + >>> bool_set(None) + False + """ + return bool(obj) + + +def if_set(set obj): + """ + >>> if_set( set() ) + False + >>> if_set( set([1]) ) + True + >>> if_set(None) + False + """ + if obj: + return True + else: + return False + + +def if_set_nogil(set obj): + """ + >>> if_set_nogil( set() ) + False + >>> if_set_nogil( set([1]) ) + True + >>> if_set_nogil(None) + False + """ + cdef bint result + with nogil: + if obj: + result = True + else: + result = False + return result + + +def if_set_literal(t): + """ + >>> if_set_literal(True) + True + >>> if_set_literal(False) + False + """ + if t: + if {1,2,3}: + return True + else: + return False + else: + if set(): + return True + else: + return False + + +def bool_frozenset(frozenset obj): + """ + >>> bool_frozenset( frozenset() ) + False + >>> bool_frozenset( frozenset([1]) ) + True + >>> bool_frozenset(None) + False + """ + return bool(obj) + + +def if_frozenset(frozenset obj): + """ + >>> if_frozenset( frozenset() ) + False + >>> if_frozenset( frozenset([1]) ) + True + >>> if_frozenset(None) + False + """ + if obj: + return True + else: + return False + + b0 = b'' b1 = b'abc' diff --git a/tests/run/c_int_types_T255.pyx b/tests/run/c_int_types_T255.pyx index 4e94309612524a8305f9eb0c1e57b54fa0dd7fa6..df79ca059784aa5a0ae5fa2201844d127edb3759 100644 --- a/tests/run/c_int_types_T255.pyx +++ b/tests/run/c_int_types_T255.pyx @@ -22,7 +22,7 @@ def test_schar(signed char x): >>> test_schar(128) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to signed char """ return x @@ -43,7 +43,7 @@ def test_add_schar(x, y): >>> test_add_schar(SCHAR_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to signed char """ cdef signed char r = x + y return r @@ -55,7 +55,7 @@ def test_uchar(unsigned char x): >>> test_uchar(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to unsigned char >>> test_uchar(0) 0 >>> test_uchar(1) @@ -65,7 +65,7 @@ def test_uchar(unsigned char x): >>> test_uchar(UCHAR_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to unsigned char """ return x @@ -76,7 +76,7 @@ def test_add_uchar(x, y): >>> test_add_uchar(UCHAR_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to unsigned char """ cdef unsigned char r = x + y return r @@ -95,7 +95,7 @@ def test_char(char x): Traceback (most recent call last): ... OverflowError: ... - >>> if CHAR_MIN < 0: + >>> if CHAR_MIN < 0: ... assert test_char(-1) == -1 >>> test_char(CHAR_MIN) == CHAR_MIN True @@ -108,7 +108,7 @@ def test_char(char x): >>> test_char(CHAR_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to char """ return x @@ -129,7 +129,7 @@ def test_add_char(x, y): >>> test_add_char(CHAR_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to char """ cdef char r = x + y return r @@ -144,7 +144,7 @@ def test_short(short x): >>> test_short(SHORT_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short >>> test_short(SHORT_MIN) == SHORT_MIN True >>> test_short(-1) @@ -158,7 +158,7 @@ def test_short(short x): >>> test_short(SHORT_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short """ return x @@ -167,7 +167,7 @@ def test_add_short(x, y): >>> test_add_short(SHORT_MIN, -1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short >>> test_add_short(SHORT_MIN, 0) == SHORT_MIN True >>> test_add_short(SHORT_MIN, 1) == SHORT_MIN+1 @@ -179,7 +179,7 @@ def test_add_short(x, y): >>> test_add_short(SHORT_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short """ cdef short r = x + y return r @@ -192,7 +192,7 @@ def test_sshort(short x): >>> test_sshort(SSHORT_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short >>> test_sshort(SSHORT_MIN) == SSHORT_MIN True >>> test_sshort(-1) @@ -206,7 +206,7 @@ def test_sshort(short x): >>> test_short(SSHORT_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to short """ return x @@ -215,7 +215,7 @@ def test_add_sshort(x, y): >>> test_add_sshort(SSHORT_MIN, -1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to signed short >>> test_add_sshort(SSHORT_MIN, 0) == SSHORT_MIN True >>> test_add_sshort(SSHORT_MIN, 1) == SSHORT_MIN+1 @@ -227,7 +227,7 @@ def test_add_sshort(x, y): >>> test_add_sshort(SSHORT_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to signed short """ cdef signed short r = x + y return r @@ -239,7 +239,7 @@ def test_ushort(unsigned short x): >>> test_ushort(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to unsigned short >>> test_ushort(0) 0 >>> test_ushort(1) @@ -260,7 +260,7 @@ def test_add_ushort(x, y): >>> test_add_ushort(USHORT_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to unsigned short """ cdef unsigned short r = x + y return r @@ -318,7 +318,7 @@ def test_add_int(x, y): SINT_MAX = <signed int>((<unsigned int>-1)>>1) SINT_MIN = (-SINT_MAX-1) -def test_sint(int x): +def test_sint(signed int x): u""" >>> test_sint(SINT_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): @@ -370,7 +370,7 @@ def test_uint(unsigned int x): >>> test_uint(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to unsigned int >>> print(test_uint(0)) 0 >>> print(test_uint(1)) @@ -449,7 +449,7 @@ def test_add_long(x, y): SLONG_MAX = <signed long>((<unsigned long>-1)>>1) SLONG_MIN = (-SLONG_MAX-1) -def test_slong(long x): +def test_slong(signed long x): u""" >>> test_slong(SLONG_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): @@ -501,7 +501,7 @@ def test_ulong(unsigned long x): >>> test_ulong(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to unsigned long >>> print(test_ulong(0)) 0 >>> print(test_ulong(1)) @@ -632,7 +632,7 @@ def test_ulonglong(unsigned long long x): >>> test_ulonglong(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to unsigned PY_LONG_LONG >>> print(test_ulonglong(0)) 0 >>> print(test_ulonglong(1)) diff --git a/tests/run/closure_inside_cdef_T554.pyx b/tests/run/closure_inside_cdef_T554.pyx index 494f17fac19ff3d4ba15fb73e00de8a617f23c98..5c84a447ffd7c49f4c403a16cc7f74a4989a7e26 100644 --- a/tests/run/closure_inside_cdef_T554.pyx +++ b/tests/run/closure_inside_cdef_T554.pyx @@ -9,6 +9,23 @@ def call_f(x): """ return f(x) + cdef f(x): # def here => works fine - def g(y): return y*x # cdef here => compile error - return g(x) # faults@ INCREF(.*cur_scope->.*v_x + def g(y): return y*x # cdef here => compile error + return g(x) # faults@ INCREF(.*cur_scope->.*v_x + + +def closure_in_void(): + """ + >>> genex = closure_in_void() + >>> list(genex) + ['a', 'b', 'c'] + """ + l = [] + add_gen(l) + return l[0] + + +cdef void add_gen(l): + x = "abc" + l.append((c for c in x)) diff --git a/tests/run/cpdef_extern_func.pyx b/tests/run/cpdef_extern_func.pyx index 8fcdda26b9f62fd3cc5d3464824bd81a1248d83f..5dffd2992c118d6d30fba906b398e5f52c1537d1 100644 --- a/tests/run/cpdef_extern_func.pyx +++ b/tests/run/cpdef_extern_func.pyx @@ -8,10 +8,9 @@ __doc__ = """ 2.0 >>> pxd_sqrt(9) 3.0 ->>> log(10) +>>> log(10) # doctest: +ELLIPSIS Traceback (most recent call last): -... -NameError: name 'log' is not defined +NameError: ...name 'log' is not defined >>> strchr('abcabc', ord('c')) 'cabc' >>> strchr(needle=ord('c'), haystack='abcabc') diff --git a/tests/run/cpp_stl_string.pyx b/tests/run/cpp_stl_string.pyx index bf4b5343754f4c65dc6298c2829ab65a461f2432..4ee1847be78fc7ee25c06edd0869e1ff8d964430 100644 --- a/tests/run/cpp_stl_string.pyx +++ b/tests/run/cpp_stl_string.pyx @@ -167,6 +167,29 @@ def test_decode(char* a): cdef string b = string(a) return b.decode('ascii') + +@cython.test_assert_path_exists("//ReturnStatNode//PythonCapiCallNode") +def test_cstr_decode(char* a): + """ + >>> print(test_cstr_decode(b_asdf)) + asdf + """ + cdef string b = string(a) + return b.c_str().decode('utf-8') + + +@cython.test_assert_path_exists("//ReturnStatNode//PythonCapiCallNode") +@cython.test_fail_if_path_exists("//ReturnStatNode//AttributeNode") +def test_cstr_ptr_decode(char* a): + """ + >>> print(test_cstr_ptr_decode(b_asdf)) + asdf + """ + cdef string b = string(a) + s = b.c_str() + return s.decode('utf-8') + + @cython.test_assert_path_exists("//PythonCapiCallNode") @cython.test_fail_if_path_exists("//AttributeNode") def test_decode_sliced(char* a): diff --git a/tests/run/cpp_template_ref_args.h b/tests/run/cpp_template_ref_args.h index fa4e770f327094dac167fa0013e7164fb2f849e1..098c6f11ebd6fb2a445e9751bfc412e00e1470a6 100644 --- a/tests/run/cpp_template_ref_args.h +++ b/tests/run/cpp_template_ref_args.h @@ -4,6 +4,7 @@ template <typename T> struct Bar { Bar & ref() { return *this; } + const Bar & const_ref() { return *this; } T value; }; diff --git a/tests/run/cpp_template_ref_args.pyx b/tests/run/cpp_template_ref_args.pyx index 526cef97af6c27d06254fc4455b202183043e040..2a37a98f18ea7675c0a169b6c4ae6c0ed2206f60 100644 --- a/tests/run/cpp_template_ref_args.pyx +++ b/tests/run/cpp_template_ref_args.pyx @@ -8,6 +8,7 @@ cdef extern from "cpp_template_ref_args.h": # bug: Bar[T] created before class fully defined T value Bar[T] & ref() except + + const Bar[T] & const_ref() except + cdef cppclass Foo[T]: Foo() @@ -33,8 +34,8 @@ def test_template_ref_arg(int x): def test_template_ref_attr(int x): """ >>> test_template_ref_attr(4) - 4 + (4, 4) """ cdef Bar[int] bar bar.value = x - return bar.ref().value + return bar.ref().value, bar.const_ref().value diff --git a/tests/run/ctypedef_int_types_T333.pyx b/tests/run/ctypedef_int_types_T333.pyx index 4cfe35b9a8240615c2d30334ba34a7cf09105254..3f1a99f69e4abdcd986c2c8435083d753adf30eb 100644 --- a/tests/run/ctypedef_int_types_T333.pyx +++ b/tests/run/ctypedef_int_types_T333.pyx @@ -25,7 +25,7 @@ def test_schar(SChar x): >>> test_schar(-129) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SChar >>> test_schar(-128) -128 >>> test_schar(0) @@ -35,7 +35,7 @@ def test_schar(SChar x): >>> test_schar(128) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SChar """ return x @@ -44,7 +44,7 @@ def test_add_schar(x, y): >>> test_add_schar(SCHAR_MIN, -1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SChar >>> test_add_schar(SCHAR_MIN, 0) == SCHAR_MIN True >>> test_add_schar(SCHAR_MIN, 1) == SCHAR_MIN+1 @@ -56,7 +56,7 @@ def test_add_schar(x, y): >>> test_add_schar(SCHAR_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SChar """ cdef SChar r = x + y return r @@ -68,7 +68,7 @@ def test_uchar(UChar x): >>> test_uchar(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to UChar >>> test_uchar(0) 0 >>> test_uchar(1) @@ -78,7 +78,7 @@ def test_uchar(UChar x): >>> test_uchar(UCHAR_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to UChar """ return x @@ -89,7 +89,7 @@ def test_add_uchar(x, y): >>> test_add_uchar(UCHAR_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to UChar """ cdef UChar r = x + y return r @@ -99,12 +99,12 @@ def test_add_uchar(x, y): SSHORT_MAX = <SShort>((<UShort>-1)>>1) SSHORT_MIN = (-SSHORT_MAX-1) -def test_sshort(short x): +def test_sshort(SShort x): u""" >>> test_sshort(SSHORT_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SShort >>> test_sshort(SSHORT_MIN) == SSHORT_MIN True >>> test_sshort(-1) @@ -127,7 +127,7 @@ def test_add_sshort(x, y): >>> test_add_sshort(SSHORT_MIN, -1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SShort >>> test_add_sshort(SSHORT_MIN, 0) == SSHORT_MIN True >>> test_add_sshort(SSHORT_MIN, 1) == SSHORT_MIN+1 @@ -139,7 +139,7 @@ def test_add_sshort(x, y): >>> test_add_sshort(SSHORT_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to SShort """ cdef SShort r = x + y return r @@ -151,7 +151,7 @@ def test_ushort(UShort x): >>> test_ushort(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to UShort >>> test_ushort(0) 0 >>> test_ushort(1) @@ -161,7 +161,7 @@ def test_ushort(UShort x): >>> test_ushort(USHORT_MAX+1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to UShort """ return x @@ -172,7 +172,7 @@ def test_add_ushort(x, y): >>> test_add_ushort(USHORT_MAX, 1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: value too large to convert to UShort """ cdef UShort r = x + y return r @@ -182,7 +182,7 @@ def test_add_ushort(x, y): SINT_MAX = <SInt>((<UInt>-1)>>1) SINT_MIN = (-SINT_MAX-1) -def test_sint(int x): +def test_sint(SInt x): u""" >>> test_sint(SINT_MIN-1) #doctest: +ELLIPSIS Traceback (most recent call last): @@ -234,7 +234,7 @@ def test_uint(UInt x): >>> test_uint(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to UInt >>> print(test_uint(0)) 0 >>> print(test_uint(1)) @@ -317,7 +317,7 @@ def test_ulong(ULong x): >>> test_ulong(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ULong >>> print(test_ulong(0)) 0 >>> print(test_ulong(1)) @@ -400,7 +400,7 @@ def test_ulonglong(ULongLong x): >>> test_ulonglong(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ULongLong >>> print(test_ulonglong(0)) 0 >>> print(test_ulonglong(1)) @@ -554,7 +554,7 @@ def test_MyUInt2(MyUInt2 x): >>> test_MyUInt2(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ... >>> test_MyUInt2(0) 0 >>> test_MyUInt2(1) @@ -582,7 +582,7 @@ def test_DefUChar(defs.UChar x): >>> test_DefUChar(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ... >>> test_DefUChar(0) 0 >>> test_DefUChar(1) @@ -606,7 +606,7 @@ def test_ExtUInt(defs.ExtUInt x): >>> test_ExtUInt(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ... >>> test_ExtUInt(0) 0 >>> test_ExtUInt(1) @@ -634,7 +634,7 @@ def test_LocUInt(LocUInt x): >>> test_LocUInt(-1) #doctest: +ELLIPSIS Traceback (most recent call last): ... - OverflowError: ... + OverflowError: can't convert negative value to ... >>> test_LocUInt(0) 0 >>> test_LocUInt(1) diff --git a/tests/run/dict.pyx b/tests/run/dict.pyx index 850cd7991de7ccae9eb97fdb42bd0286bb1b5020..ea93e4c557228ba144ae373e6e8af8b2f79647ab 100644 --- a/tests/run/dict.pyx +++ b/tests/run/dict.pyx @@ -1,3 +1,8 @@ +import sys + +IS_PY35 = sys.version_info >= (3, 5) + + def empty(): """ >>> empty() @@ -90,15 +95,15 @@ def item_creation_sideeffect(L, sideeffect, unhashable): >>> L = [] >>> item_creation_sideeffect(L, sideeffect, unhashable) # doctest: +ELLIPSIS Traceback (most recent call last): - TypeError:... unhashable ... + TypeError: ...unhashable... >>> L [2, 4] >>> L = [] >>> {1:2, sideeffect(2): 3, 3: 4, unhashable(4): 5, sideeffect(5): 6} # doctest: +ELLIPSIS Traceback (most recent call last): - TypeError:... unhashable ... - >>> L - [2, 4] + TypeError: ...unhashable... + >>> L if IS_PY35 else (L + [5]) + [2, 4, 5] """ return {1:2, sideeffect(2): 3, 3: 4, unhashable(4): 5, sideeffect(5): 6} diff --git a/tests/run/embedsignatures.pyx b/tests/run/embedsignatures.pyx index 0bfebfefbaf30068a58202c6a3c36e4cfbe50bcc..781cd21181a76d1d0096446194248329dbfe61bb 100644 --- a/tests/run/embedsignatures.pyx +++ b/tests/run/embedsignatures.pyx @@ -199,6 +199,9 @@ __doc__ = ur""" >>> print(funcdoc(f_defexpr5)) f_defexpr5(int x=4) + + >>> print(funcdoc(f_charptr_null)) + f_charptr_null(char *s=NULL) -> char * """ cdef class Ext: @@ -403,6 +406,10 @@ cpdef f_defexpr4(int x = (Ext.CONST1 + FLAG1) * Ext.CONST2): cpdef f_defexpr5(int x = 2+2): pass +cpdef (char*) f_charptr_null(char* s=NULL): + return s or b'abc' + + # no signatures for lambda functions lambda_foo = lambda x: 10 lambda_bar = lambda x: 20 diff --git a/tests/run/float_division.pyx b/tests/run/float_division.pyx new file mode 100644 index 0000000000000000000000000000000000000000..36d6009384608eef2a3c510b1fd1c4c18803d073 --- /dev/null +++ b/tests/run/float_division.pyx @@ -0,0 +1,200 @@ +# mode: run +# tag: division + + +def int_by_float(): + """ + >>> int_by_float() + 0.5 + """ + return 1 / 2.0 + + +def float_by_int(): + """ + >>> float_by_int() + 2.0 + """ + return 2.0 / 1 + + +def float_by_float(): + """ + >>> float_by_float() + 1.5 + """ + return 3.0 / 2.0 + + +def div_1_by(x): + """ + >>> div_1_by(1.0) + 1.0 + >>> div_1_by(2.0) + 0.5 + >>> div_1_by(0.5) + 2.0 + >>> 1.0 / float('inf') + 0.0 + >>> div_1_by(float('inf')) + 0.0 + >>> div_1_by(float('-inf')) + -0.0 + >>> div_1_by(float('nan')) + nan + """ + return 1.0 / x + + +def div_by_2(x): + """ + >>> div_by_2(1.0) + 0.5 + >>> float('inf') / 2.0 + inf + >>> div_by_2(float('inf')) + inf + >>> div_by_2(float('-inf')) + -inf + >>> float('nan') / 2.0 + nan + >>> div_by_2(float('nan')) + nan + """ + return x / 2.0 + + +def div_by_neg_2(x): + """ + >>> div_by_neg_2(1.0) + -0.5 + >>> div_by_neg_2(-1.0) + 0.5 + >>> (-2**14) / (-2.0) + 8192.0 + >>> div_by_neg_2(-2**14) + 8192.0 + >>> (-2**52) / (-2.0) + 2251799813685248.0 + >>> div_by_neg_2(-2**52) + 2251799813685248.0 + >>> (-2**53-1) / (-2.0) + 4503599627370496.0 + >>> div_by_neg_2(-2**53-1) + 4503599627370496.0 + >>> float('inf') / -2.0 + -inf + >>> div_by_neg_2(float('inf')) + -inf + >>> div_by_neg_2(float('-inf')) + inf + >>> float('nan') / -2.0 + nan + >>> div_by_neg_2(float('nan')) + nan + """ + return x / -2.0 + + +def div_neg_2_by(x): + """ + >>> div_neg_2_by(1.0) + -2.0 + >>> div_neg_2_by(-1) + 2.0 + >>> div_neg_2_by(-2.0) + 1.0 + >>> div_neg_2_by(-2) + 1.0 + >>> -2.0 / float('inf') + -0.0 + >>> div_neg_2_by(float('inf')) + -0.0 + >>> div_neg_2_by(float('-inf')) + 0.0 + >>> float('nan') / -2.0 + nan + >>> div_neg_2_by(float('nan')) + nan + """ + return (-2.0) / x + + +def div_by_nan(x): + """ + >>> 1.0 / float('nan') + nan + >>> div_by_nan(1.0) + nan + >>> float('nan') / float('nan') + nan + >>> div_by_nan(float('nan')) + nan + >>> float('inf') / float('nan') + nan + >>> div_by_nan(float('inf')) + nan + """ + return x / float("nan") + + +def div_nan_by(x): + """ + >>> float('nan') / 1.0 + nan + >>> div_nan_by(1.0) + nan + >>> float('nan') / float('nan') + nan + >>> div_nan_by(float('nan')) + nan + """ + return float("nan") / x + + +def div_by_inf(x): + """ + >>> 1 / float('inf') + 0.0 + >>> div_by_inf(1) + 0.0 + >>> 1.0 / float('inf') + 0.0 + >>> div_by_inf(1.0) + 0.0 + >>> div_by_inf(float('inf')) + nan + """ + return x / float("inf") + + +def div_inf_by(x): + """ + >>> float('inf') / 1.0 + inf + >>> div_inf_by(1.0) + inf + >>> float('inf') / float('nan') + nan + >>> div_inf_by(float('nan')) + nan + >>> float('inf') / float('-inf') + nan + >>> div_inf_by(float('-inf')) + nan + """ + return float("inf") / x + + +def div_neg_inf_by(x): + """ + >>> float('-inf') / 1.0 + -inf + >>> div_neg_inf_by(1.0) + -inf + >>> float('-inf') / -1.0 + inf + >>> div_neg_inf_by(-1.0) + inf + """ + return float("-inf") / x diff --git a/tests/run/future_division.pyx b/tests/run/future_division.pyx index e67bc091167643aa51e9a9215d7b18d170edcc84..5820dca5d308667777b98eb70980b93b1e995b31 100644 --- a/tests/run/future_division.pyx +++ b/tests/run/future_division.pyx @@ -36,15 +36,49 @@ def constants(): """ return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2 + def py_mix(a): """ >>> py_mix(1) (0.5, 0, 0.5, 0.0, 0.5, 0) >>> py_mix(1.0) (0.5, 0.0, 0.5, 0.0, 0.5, 0.0) + >>> 2**53 / 2.0 + 4503599627370496.0 + >>> py_mix(2**53) + (4503599627370496.0, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496) + >>> py_mix(2**53 + 1) + (4503599627370496.0, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496) + >>> py_mix(2**53 + 1.0) + (4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0) """ return a/2, a//2, a/2.0, a//2.0, a/2, a//2 + +def py_mix_by_neg1(a): + """ + >>> py_mix_by_neg1(0) + (-0.0, 0, -0.0, -0.0, -0.0, 0) + >>> py_mix_by_neg1(-1) + (1.0, 1, 1.0, 1.0, 1.0, 1) + >>> py_mix_by_neg1(int(2**31-1)) + (-2147483647.0, -2147483647, -2147483647.0, -2147483647.0, -2147483647.0, -2147483647) + >>> py_mix_by_neg1(int(-2**31-1)) + (2147483649.0, 2147483649, 2147483649.0, 2147483649.0, 2147483649.0, 2147483649) + >>> results = py_mix_by_neg1(int(2**63-1)) + >>> results[0] == results[2] == results[3] == results[4] == float(2**63-1) / -1.0 or results + True + >>> results[1] == results[5] == (2**63-1) // -1 or results + True + >>> results = py_mix_by_neg1(int(-2**63-1)) + >>> results[0] == results[2] == results[3] == results[4] == float(-2**63-1) / -1.0 or results + True + >>> results[1] == results[5] == (-2**63-1) // -1 or results + True + """ + return a/-1, a//-1, a/-1.0, a//-1.0, a/-1, a//-1 + + def py_mix_rev(a): """ >>> py_mix_rev(4) diff --git a/tests/run/generators.pyx b/tests/run/generators.pyx index df7c4e20f99e487b92f576b48b87b01c56601095..6736694253798dd754c875a14e84f7a8ef2a6425 100644 --- a/tests/run/generators.pyx +++ b/tests/run/generators.pyx @@ -1,6 +1,12 @@ # mode: run # tag: generators +try: + from collections.abc import Generator +except ImportError: + from collections import Generator + + def very_simple(): """ >>> x = very_simple() @@ -293,6 +299,7 @@ def test_decorated(*args): for i in args: yield i + def test_return(a): """ >>> d = dict() @@ -309,6 +316,76 @@ def test_return(a): a['i_was_here'] = True return + +def test_return_in_finally(a): + """ + >>> d = dict() + >>> obj = test_return_in_finally(d) + >>> next(obj) + 1 + >>> next(obj) + Traceback (most recent call last): + StopIteration + >>> d['i_was_here'] + True + + >>> d = dict() + >>> obj = test_return_in_finally(d) + >>> next(obj) + 1 + >>> obj.send(2) + Traceback (most recent call last): + StopIteration + >>> d['i_was_here'] + True + + >>> obj = test_return_in_finally(None) + >>> next(obj) + 1 + >>> next(obj) + Traceback (most recent call last): + StopIteration + + >>> obj = test_return_in_finally(None) + >>> next(obj) + 1 + >>> obj.send(2) + Traceback (most recent call last): + StopIteration + """ + yield 1 + try: + a['i_was_here'] = True + finally: + return + + +def test_return_none_in_finally(a): + """ + >>> d = dict() + >>> obj = test_return_none_in_finally(d) + >>> next(obj) + 1 + >>> next(obj) + Traceback (most recent call last): + StopIteration + >>> d['i_was_here'] + True + + >>> obj = test_return_none_in_finally(None) + >>> next(obj) + 1 + >>> next(obj) + Traceback (most recent call last): + StopIteration + """ + yield 1 + try: + a['i_was_here'] = True + finally: + return None + + def test_copied_yield(foo): """ >>> class Manager(object): @@ -396,3 +473,21 @@ def test_double_with_gil_section(): pass with gil: pass + + +def test_generator_abc(): + """ + >>> isinstance(test_generator_abc(), Generator) + True + + >>> try: + ... from collections.abc import Generator + ... except ImportError: + ... from collections import Generator + + >>> isinstance(test_generator_abc(), Generator) + True + >>> isinstance((lambda:(yield))(), Generator) + True + """ + yield 1 diff --git a/tests/run/generators_py.py b/tests/run/generators_py.py index 286f26b0d8b67cd19d1b67e764c472cb05beb80d..152e06a39c410491ee73e05ab39b77b61c0ddd21 100644 --- a/tests/run/generators_py.py +++ b/tests/run/generators_py.py @@ -3,12 +3,6 @@ import cython -try: - from builtins import next # Py3k -except ImportError: - def next(it): - return it.next() - def very_simple(): """ diff --git a/tests/run/ifelseexpr_T267.pyx b/tests/run/ifelseexpr_T267.pyx index 270d7f33d0e11b18ef24c73e6d05c32b46398359..24dabc442cefb27ae39eb007f110e79391730490 100644 --- a/tests/run/ifelseexpr_T267.pyx +++ b/tests/run/ifelseexpr_T267.pyx @@ -25,6 +25,7 @@ def temps(x): """ return ident(1) if ident(x) < ident(5) else ident(10) + def nested(x): """ >>> nested(1) @@ -34,7 +35,9 @@ def nested(x): >>> nested(3) 3 """ - return 1 if x == 1 else (2 if x == 2 else 3) + a = 1 if x == 1 else (2 if x == 2 else 3) + return a + @cython.test_fail_if_path_exists('//CondExprNode') def const_true(a,b): diff --git a/tests/run/index.pyx b/tests/run/index.pyx index 539d13d062f0b1010bf45379db88733cee60ceda..a0bbbd6248aa80e8e50ef44a3a6d12ad450b7812 100644 --- a/tests/run/index.pyx +++ b/tests/run/index.pyx @@ -83,12 +83,12 @@ def del_index_list(list L, Py_ssize_t index): [0, 2, 3] >>> del_index_list(list(range(4)), -1) [0, 1, 2] - >>> del_index_list(list(range(4)), py_maxsize) + >>> del_index_list(list(range(4)), py_maxsize) # doctest: +ELLIPSIS Traceback (most recent call last): - IndexError: list assignment index out of range - >>> del_index_list(list(range(4)), -py_maxsize) + IndexError: list... index out of range + >>> del_index_list(list(range(4)), -py_maxsize) # doctest: +ELLIPSIS Traceback (most recent call last): - IndexError: list assignment index out of range + IndexError: list... index out of range """ del L[index] return L @@ -102,12 +102,12 @@ def set_index_list(list L, Py_ssize_t index): [0, 5, 2, 3] >>> set_index_list(list(range(4)), -1) [0, 1, 2, 5] - >>> set_index_list(list(range(4)), py_maxsize) + >>> set_index_list(list(range(4)), py_maxsize) # doctest: +ELLIPSIS Traceback (most recent call last): - IndexError: list assignment index out of range - >>> set_index_list(list(range(4)), -py_maxsize) + IndexError: list... index out of range + >>> set_index_list(list(range(4)), -py_maxsize) # doctest: +ELLIPSIS Traceback (most recent call last): - IndexError: list assignment index out of range + IndexError: list... index out of range """ L[index] = 5 return L diff --git a/tests/run/int128.pyx b/tests/run/int128.pyx new file mode 100644 index 0000000000000000000000000000000000000000..cb18ccbd8774ab3502cb8c68eb90c34818965243 --- /dev/null +++ b/tests/run/int128.pyx @@ -0,0 +1,119 @@ + +cdef extern from *: + ctypedef long long int128_t "__int128_t" + ctypedef unsigned long long uint128_t "__uint128_t" + + +def bigint(x): + print(str(x).rstrip('L')) + + +def unsigned_conversion(x): + """ + >>> bigint(unsigned_conversion(0)) + 0 + >>> bigint(unsigned_conversion(2)) + 2 + + >>> unsigned_conversion(-2) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: can't convert negative value to ...uint128_t + >>> unsigned_conversion(-2**120) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: can't convert negative value to ...uint128_t + >>> unsigned_conversion(-2**127) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: can't convert negative value to ...uint128_t + >>> unsigned_conversion(-2**128) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: can't convert negative value to ...uint128_t + + >>> bigint(unsigned_conversion(2**20)) + 1048576 + >>> bigint(unsigned_conversion(2**30-1)) + 1073741823 + >>> bigint(unsigned_conversion(2**30)) + 1073741824 + >>> bigint(unsigned_conversion(2**30+1)) + 1073741825 + + >>> bigint(2**60) + 1152921504606846976 + >>> bigint(unsigned_conversion(2**60-1)) + 1152921504606846975 + >>> bigint(unsigned_conversion(2**60)) + 1152921504606846976 + >>> bigint(unsigned_conversion(2**60+1)) + 1152921504606846977 + >>> bigint(2**64) + 18446744073709551616 + >>> bigint(unsigned_conversion(2**64)) + 18446744073709551616 + + >>> bigint(2**120) + 1329227995784915872903807060280344576 + >>> bigint(unsigned_conversion(2**120)) + 1329227995784915872903807060280344576 + >>> bigint(2**128-1) + 340282366920938463463374607431768211455 + >>> bigint(unsigned_conversion(2**128-1)) + 340282366920938463463374607431768211455 + >>> bigint(unsigned_conversion(2**128)) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: ... too big to convert + """ + cdef uint128_t n = x + return n + + +def signed_conversion(x): + """ + >>> bigint(signed_conversion(0)) + 0 + >>> bigint(signed_conversion(2)) + 2 + >>> bigint(signed_conversion(-2)) + -2 + + >>> bigint(signed_conversion(2**20)) + 1048576 + >>> bigint(signed_conversion(2**32)) + 4294967296 + >>> bigint(2**64) + 18446744073709551616 + >>> bigint(signed_conversion(2**64)) + 18446744073709551616 + >>> bigint(signed_conversion(-2**64)) + -18446744073709551616 + + >>> bigint(2**118) + 332306998946228968225951765070086144 + >>> bigint(signed_conversion(2**118)) + 332306998946228968225951765070086144 + >>> bigint(signed_conversion(-2**118)) + -332306998946228968225951765070086144 + + >>> bigint(2**120) + 1329227995784915872903807060280344576 + >>> bigint(signed_conversion(2**120)) + 1329227995784915872903807060280344576 + >>> bigint(signed_conversion(-2**120)) + -1329227995784915872903807060280344576 + + >>> bigint(2**127-1) + 170141183460469231731687303715884105727 + >>> bigint(signed_conversion(2**127-2)) + 170141183460469231731687303715884105726 + >>> bigint(signed_conversion(2**127-1)) + 170141183460469231731687303715884105727 + >>> bigint(signed_conversion(2**127)) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: ... too big to convert + >>> bigint(signed_conversion(-2**127)) + -170141183460469231731687303715884105728 + >>> bigint(signed_conversion(-2**127-1)) # doctest: +ELLIPSIS + Traceback (most recent call last): + OverflowError: ... too big to convert + """ + cdef int128_t n = x + return n diff --git a/tests/run/kwargs_passthrough.pyx b/tests/run/kwargs_passthrough.pyx new file mode 100644 index 0000000000000000000000000000000000000000..704638a55a1a87bb87c84026b0611358a76a1ed1 --- /dev/null +++ b/tests/run/kwargs_passthrough.pyx @@ -0,0 +1,238 @@ +cimport cython + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def wrap_passthrough(f): + """ + >>> def f(a=1): return a + >>> wrapped = wrap_passthrough(f) + >>> wrapped(1) + CALLED + 1 + >>> wrapped(a=2) + CALLED + 2 + """ + def wrapper(*args, **kwargs): + print("CALLED") + return f(*args, **kwargs) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def unused(*args, **kwargs): + """ + >>> unused() + () + >>> unused(1, 2) + (1, 2) + """ + return args + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def used_in_closure(**kwargs): + """ + >>> used_in_closure() + >>> d = {} + >>> used_in_closure(**d) + >>> d # must not be modified + {} + """ + def func(): + kwargs['test'] = 1 + return func() + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def modify_in_closure(**kwargs): + """ + >>> func = modify_in_closure() + >>> func() + + >>> d = {} + >>> func = modify_in_closure(**d) + >>> func() + >>> d # must not be modified + {} + """ + def func(): + kwargs['test'] = 1 + return func + + +@cython.test_assert_path_exists('//MergedDictNode') +def wrap_passthrough_more(f): + """ + >>> def f(a=1, test=2): + ... return a, test + >>> wrapped = wrap_passthrough_more(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (2, 1) + """ + def wrapper(*args, **kwargs): + print("CALLED") + return f(*args, test=1, **kwargs) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def wrap_passthrough2(f): + """ + >>> def f(a=1): return a + >>> wrapped = wrap_passthrough2(f) + >>> wrapped(1) + CALLED + 1 + >>> wrapped(a=2) + CALLED + 2 + """ + def wrapper(*args, **kwargs): + print("CALLED") + f(*args, **kwargs) + return f(*args, **kwargs) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def wrap_modify(f): + """ + >>> def f(a=1, test=2): + ... return a, test + + >>> wrapped = wrap_modify(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (2, 1) + >>> wrapped(a=2, test=3) + CALLED + (2, 1) + """ + def wrapper(*args, **kwargs): + print("CALLED") + kwargs['test'] = 1 + return f(*args, **kwargs) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def wrap_modify_mix(f): + """ + >>> def f(a=1, test=2): + ... return a, test + + >>> wrapped = wrap_modify_mix(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (2, 1) + >>> wrapped(a=2, test=3) + CALLED + (2, 1) + """ + def wrapper(*args, **kwargs): + print("CALLED") + f(*args, **kwargs) + kwargs['test'] = 1 + return f(*args, **kwargs) + return wrapper + + +@cython.test_assert_path_exists('//MergedDictNode') +def wrap_modify_func(f): + """ + >>> def f(a=1, test=2): + ... return a, test + + >>> wrapped = wrap_modify_func(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (2, 1) + >>> wrapped(a=2, test=3) + CALLED + (2, 1) + """ + def modify(kw): + kw['test'] = 1 + return kw + + def wrapper(*args, **kwargs): + print("CALLED") + return f(*args, **modify(kwargs)) + return wrapper + + +@cython.test_assert_path_exists('//MergedDictNode') +def wrap_modify_func_mix(f): + """ + >>> def f(a=1, test=2): + ... return a, test + + >>> wrapped = wrap_modify_func_mix(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (2, 1) + >>> wrapped(a=2, test=3) + CALLED + (2, 1) + """ + def modify(kw): + kw['test'] = 1 + return kw + + def wrapper(*args, **kwargs): + print("CALLED") + f(*args, **kwargs) + return f(*args, **modify(kwargs)) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def wrap_reassign(f): + """ + >>> def f(a=1, test=2): + ... return a, test + + >>> wrapped = wrap_reassign(f) + >>> wrapped(1) + CALLED + (1, 1) + >>> wrapped(a=2) + CALLED + (1, 1) + >>> wrapped(a=2, test=3) + CALLED + (1, 1) + """ + def wrapper(*args, **kwargs): + print("CALLED") + kwargs = {'test': 1} + return f(*args, **kwargs) + return wrapper + + +@cython.test_fail_if_path_exists('//MergedDictNode') +def kwargs_metaclass(**kwargs): + """ + >>> K = kwargs_metaclass() + >>> K = kwargs_metaclass(metaclass=type) + """ + class K(**kwargs): + pass + return K diff --git a/tests/run/libcpp_all.pyx b/tests/run/libcpp_all.pyx index 97cc5ab75fba0346de7b38abfb858a09dec90da2..de75797de3c903c350f557771eb5a5779a67094c 100644 --- a/tests/run/libcpp_all.pyx +++ b/tests/run/libcpp_all.pyx @@ -1,5 +1,7 @@ # tag: cpp +import cython + cimport libcpp cimport libcpp.deque @@ -10,6 +12,7 @@ cimport libcpp.queue cimport libcpp.set cimport libcpp.stack cimport libcpp.vector +cimport libcpp.complex from libcpp.deque cimport * from libcpp.list cimport * @@ -19,6 +22,7 @@ from libcpp.queue cimport * from libcpp.set cimport * from libcpp.stack cimport * from libcpp.vector cimport * +from libcpp.complex cimport * cdef libcpp.deque.deque[int] d1 = deque[int]() cdef libcpp.list.list[int] l1 = list[int]() @@ -67,3 +71,23 @@ def test_vector_coercion(*args): for a in args: v.push_back(a) return [v[0][i] for i in range(v.size())] + +def test_const_vector(*args): + """ + >>> test_const_vector(1.75) + [1.75] + >>> test_const_vector(1, 10, 100) + [1.0, 10.0, 100.0] + """ + cdef vector[double] v + for a in args: + v.push_back(a) + return const_vector_to_list(v) + +cdef const_vector_to_list(const vector[double]& cv): + cdef vector[double].const_iterator iter = cv.const_begin() + cdef lst = [] + while iter != cv.const_end(): + lst.append(cython.operator.dereference(iter)) + cython.operator.preincrement(iter) + return lst diff --git a/tests/run/min_max_optimization.pyx b/tests/run/min_max_optimization.pyx index e20106318d3cef95b5becfbe180febcec36114a0..9cb25d4e5f3af3dede5ce37ae0d281b73de195db 100644 --- a/tests/run/min_max_optimization.pyx +++ b/tests/run/min_max_optimization.pyx @@ -165,3 +165,26 @@ def test_minN(): print min(my_int, 2, my_int, 0, my_pyint, my_int, len(my_list)) print min(my_int, my_int, 0, my_pyint, my_int, len(my_list)) print min(my_int, my_int, 2, my_int, 0, my_pyint, my_int, len(my_list)) + + +''' +# ticket 772 +# FIXME: signed vs. unsigned fails to safely handle intermediate results + +@cython.test_assert_path_exists("//CondExprNode") +@cython.test_fail_if_path_exists("//SimpleCallNode") +def max3_typed_signed_unsigned(int a, unsigned int b, int c): + """ + >>> max3_typed_signed_unsigned(1,2,-3) + 2 + >>> max3_typed_signed_unsigned(-2,3,1) + 3 + >>> max3_typed_signed_unsigned(-2,1,-3) + 1 + >>> max3_typed_signed_unsigned(3,-1,2) + 3 + >>> max3_typed_signed_unsigned(-3,2,1) + 2 + """ + return max(a,b,c) +''' diff --git a/tests/run/modop.pyx b/tests/run/modop.pyx index 915359b7f574a9d992747e2dc112be7c00614ec5..96f4b77f2c958b600b0f8cd7a99cbfb6ec56d650 100644 --- a/tests/run/modop.pyx +++ b/tests/run/modop.pyx @@ -1,9 +1,5 @@ import sys -if sys.version_info[0] < 3: - __doc__ = u""" - >>> modptr() - 'spameggs' - """ + def modobj(obj2, obj3): """ @@ -15,6 +11,187 @@ def modobj(obj2, obj3): obj1 = obj2 % obj3 return obj1 + +def mod_obj_10(int2): + """ + >>> 0 % 10 + 0 + >>> mod_obj_10(0) + 0 + >>> 1 % 10 + 1 + >>> mod_obj_10(1) + 1 + >>> (-1) % 10 + 9 + >>> mod_obj_10(-1) + 9 + >>> 9 % 10 + 9 + >>> mod_obj_10(9) + 9 + >>> 10 % 10 + 0 + >>> mod_obj_10(10) + 0 + >>> (-10) % 10 + 0 + >>> mod_obj_10(-10) + 0 + >>> (-12) % 10 + 8 + >>> mod_obj_10(-12) + 8 + >>> 10002 % 10 + 2 + >>> mod_obj_10(10002) + 2 + >>> int((2**25) % 10) + 2 + >>> int(mod_obj_10(2**25)) + 2 + >>> int((-2**25) % 10) + 8 + >>> int(mod_obj_10(-2**25)) + 8 + >>> int((-2**31-1) % 10) + 1 + >>> int(mod_obj_10(int(-2**31-1))) + 1 + >>> int((2**50) % 10) + 4 + >>> int(mod_obj_10(2**50)) + 4 + >>> int((-2**50) % 10) + 6 + >>> int(mod_obj_10(-2**50)) + 6 + >>> int((-2**63-1) % 10) + 1 + >>> int(mod_obj_10(-2**63-1)) + 1 + >>> int((2**200) % 10) + 6 + >>> int(mod_obj_10(2**200)) + 6 + >>> int((-2**200) % 10) + 4 + >>> int(mod_obj_10(-2**200)) + 4 + """ + int1 = int2 % 10 + return int1 + + +def mod_obj_17(int2): + """ + >>> 0 % 17 + 0 + >>> mod_obj_17(0) + 0 + >>> 1 % 17 + 1 + >>> mod_obj_17(1) + 1 + >>> (-1) % 17 + 16 + >>> mod_obj_17(-1) + 16 + >>> 9 % 17 + 9 + >>> mod_obj_17(16) + 16 + >>> 17 % 17 + 0 + >>> mod_obj_17(17) + 0 + >>> (-17) % 17 + 0 + >>> mod_obj_17(-17) + 0 + >>> (-18) % 17 + 16 + >>> mod_obj_17(-18) + 16 + >>> 10002 % 17 + 6 + >>> mod_obj_17(10002) + 6 + >>> int((2**25) % 17) + 2 + >>> int(mod_obj_17(2**25)) + 2 + >>> int((-2**25) % 17) + 15 + >>> int(mod_obj_17(-2**25)) + 15 + >>> int((-2**31-1) % 17) + 7 + >>> int(mod_obj_17(int(-2**31-1))) + 7 + >>> int((2**50) % 17) + 4 + >>> int(mod_obj_17(2**50)) + 4 + >>> int((-2**50) % 17) + 13 + >>> int(mod_obj_17(-2**50)) + 13 + >>> int((-2**63-1) % 17) + 7 + >>> int(mod_obj_17(-2**63-1)) + 7 + >>> int((2**200) % 17) + 1 + >>> int(mod_obj_17(2**200)) + 1 + >>> int((-2**200) % 17) + 16 + >>> int(mod_obj_17(-2**200)) + 16 + """ + int1 = int2 % 17 + return int1 + + +def mod_obj_m2(int2): + """ + >>> 0 % -2 + 0 + >>> mod_obj_m2(0) + 0 + >>> 1 % -2 + -1 + >>> mod_obj_m2(1) + -1 + >>> 9 % -2 + -1 + >>> mod_obj_m2(9) + -1 + """ + int1 = int2 % -2 + return int1 + + +def mod_obj_m2f(obj2): + """ + >>> 0 % -2.0 == 0.0 # -0.0 in Py2.7+ + True + >>> mod_obj_m2f(0) + -0.0 + >>> 1 % -2.0 + -1.0 + >>> mod_obj_m2f(1) + -1.0 + >>> 9 % -2.0 + -1.0 + >>> mod_obj_m2f(9) + -1.0 + """ + result = obj2 % -2.0 + return result + + def modint(int int2, int int3): """ >>> modint(9,2) @@ -24,9 +201,14 @@ def modint(int int2, int int3): int1 = int2 % int3 return int1 + def modptr(): + """ + >>> print(modptr() if sys.version_info[0] < 3 else 'spameggs') + spameggs + """ cdef char *str2, *str3 str2 = "spam%s" str3 = "eggs" - obj1 = str2 % str3 # '%' operator doesn't work on byte strings in Py3 + obj1 = str2 % str3 # '%' operator doesn't work on byte strings in Py3 return obj1 diff --git a/tests/run/non_dict_kwargs_T470.pyx b/tests/run/non_dict_kwargs_T470.pyx index aae6c736b577475e196fb3534c795e22e3f5cbaf..b60d5621008164dd70eb996e9fe2224e7a86bcea 100644 --- a/tests/run/non_dict_kwargs_T470.pyx +++ b/tests/run/non_dict_kwargs_T470.pyx @@ -1,31 +1,16 @@ +# mode: run # ticket: 470 -__doc__ = u""" ->>> func(**{'a' : 7}) -True ->>> func(**SubDict()) -True - ->>> call_non_dict_test() -True ->>> call_non_dict_test_kw() -True - ->>> call_sub_dict_test() -True ->>> call_sub_dict_test_kw() -True -""" - -import sys - -if sys.version_info >= (2,6): - __doc__ += u""" ->>> func(**NonDict()) -True -""" def func(**kwargs): + """ + >>> func(**{'a' : 7}) + True + >>> func(**SubDict()) + True + >>> func(**NonDict()) + True + """ return type(kwargs) is dict and kwargs['a'] == 7 @@ -37,9 +22,17 @@ class NonDict(object): return ['a'] def call_non_dict_test(): + """ + >>> call_non_dict_test() + True + """ return func(**NonDict()) def call_non_dict_test_kw(): + """ + >>> call_non_dict_test_kw() + True + """ return func(b=5, **NonDict()) @@ -48,7 +41,15 @@ class SubDict(dict): self['a'] = 7 def call_sub_dict_test(): + """ + >>> call_sub_dict_test() + True + """ return func(**SubDict()) def call_sub_dict_test_kw(): + """ + >>> call_sub_dict_test_kw() + True + """ return func(b=5, **SubDict()) diff --git a/tests/run/non_future_division.pyx b/tests/run/non_future_division.pyx index 2d93551b247b14335657965d6143f301289d165d..4c91413d3c235edba126909250a7d84bb8cbcdfa 100644 --- a/tests/run/non_future_division.pyx +++ b/tests/run/non_future_division.pyx @@ -36,15 +36,49 @@ def constants(): """ return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2 + def py_mix(a): """ >>> py_mix(1) (0, 0, 0.5, 0.0, 0, 0) >>> py_mix(1.0) (0.5, 0.0, 0.5, 0.0, 0.5, 0.0) + >>> 2**53 / 2.0 + 4503599627370496.0 + >>> py_mix(2**53) + (4503599627370496, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496, 4503599627370496) + >>> py_mix(2**53 + 1) + (4503599627370496, 4503599627370496, 4503599627370496.0, 4503599627370496.0, 4503599627370496, 4503599627370496) + >>> py_mix(2**53 + 1.0) + (4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0, 4503599627370496.0) """ return a/2, a//2, a/2.0, a//2.0, a/2, a//2 + +def py_mix_by_neg1(a): + """ + >>> py_mix_by_neg1(0) + (0, 0, -0.0, -0.0, 0, 0) + >>> py_mix_by_neg1(-1) + (1, 1, 1.0, 1.0, 1, 1) + >>> py_mix_by_neg1(int(2**31-1)) + (-2147483647, -2147483647, -2147483647.0, -2147483647.0, -2147483647, -2147483647) + >>> py_mix_by_neg1(int(-2**31-1)) + (2147483649, 2147483649, 2147483649.0, 2147483649.0, 2147483649, 2147483649) + >>> results = py_mix_by_neg1(int(2**63-1)) + >>> results[2] == results[3] == float(2**63-1) / -1.0 or results + True + >>> results[0] == results[1] == results[4] == results[5] == (2**63-1) // -1 or results + True + >>> results = py_mix_by_neg1(int(-2**63-1)) + >>> results[2] == results[3] == float(-2**63-1) / -1.0 or results + True + >>> results[0] == results[1] == results[4] == results[5] == (-2**63-1) // -1 or results + True + """ + return a/-1, a//-1, a/-1.0, a//-1.0, a/-1, a//-1 + + def py_mix_rev(a): """ >>> py_mix_rev(4) diff --git a/tests/run/onelinesuite.py b/tests/run/onelinesuite.py index bc9b02153fd0f34d09c2e6b40e275a7924dffe52..1e46571610a422f19e20aa0945163f172b9b31d5 100644 --- a/tests/run/onelinesuite.py +++ b/tests/run/onelinesuite.py @@ -2,12 +2,12 @@ # tag: syntax """ ->>> y +>>> y # doctest: +ELLIPSIS Traceback (most recent call last): -NameError: name 'y' is not defined ->>> z +NameError: ...name 'y' is not defined +>>> z # doctest: +ELLIPSIS Traceback (most recent call last): -NameError: name 'z' is not defined +NameError: ...name 'z' is not defined >>> f() 17 """ diff --git a/tests/run/pep448_extended_unpacking.pyx b/tests/run/pep448_extended_unpacking.pyx new file mode 100644 index 0000000000000000000000000000000000000000..2f27c62cacacadafca90451fd6bde6b988d49b09 --- /dev/null +++ b/tests/run/pep448_extended_unpacking.pyx @@ -0,0 +1,520 @@ + +cimport cython + + +class Iter(object): + def __init__(self, it=()): + self.it = iter(it) + def __iter__(self): + return self + def __next__(self): + return next(self.it) + next = __next__ + + +class Map(object): + def __init__(self, mapping={}): + self.mapping = mapping + def __iter__(self): + return iter(self.mapping) + def keys(self): + return self.mapping.keys() + def __getitem__(self, key): + return self.mapping[key] + + +#### tuples + + +@cython.test_fail_if_path_exists( + "//TupleNode//TupleNode", + "//MergedSequenceNode", +) +def unpack_tuple_literal(): + """ + >>> unpack_tuple_literal() + (1, 2, 4, 5) + """ + return (*(1, 2, *(4, 5)),) + + +def unpack_tuple_literal_mult(): + """ + >>> unpack_tuple_literal_mult() + (1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5) + """ + return (*((1, 2, *((4, 5) * 2)) * 3),) + + +@cython.test_fail_if_path_exists( + "//TupleNode//TupleNode", + "//MergedSequenceNode", +) +def unpack_tuple_literal_empty(): + """ + >>> unpack_tuple_literal_empty() + () + """ + return (*(*(), *()), *(), *(*(*(),),)) + + +def unpack_tuple_simple(it): + """ + >>> unpack_tuple_simple([]) + () + >>> unpack_tuple_simple(set()) + () + >>> unpack_tuple_simple(Iter()) + () + + >>> unpack_tuple_simple([1]) + (1,) + + >>> unpack_tuple_simple([2, 1]) + (2, 1) + >>> unpack_tuple_simple((2, 1)) + (2, 1) + >>> sorted(unpack_tuple_simple(set([2, 1]))) + [1, 2] + >>> unpack_tuple_simple(Iter([2, 1])) + (2, 1) + """ + return (*it,) + + +def unpack_tuple_from_iterable(it): + """ + >>> unpack_tuple_from_iterable([1, 2, 3]) + (1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3) + >>> unpack_tuple_from_iterable((1, 2, 3)) + (1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3) + >>> sorted(unpack_tuple_from_iterable(set([1, 2, 3]))) + [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3] + + >>> unpack_tuple_from_iterable([1, 2]) + (1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2) + >>> sorted(unpack_tuple_from_iterable(set([1, 2]))) + [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2] + >>> unpack_tuple_from_iterable(Iter([1, 2])) + (1, 2, 1, 2, 1, 2, 1) + + >>> unpack_tuple_from_iterable([3]) + (1, 2, 3, 1, 3, 3, 3, 2, 1, 3) + >>> unpack_tuple_from_iterable(set([3])) + (1, 2, 3, 1, 3, 3, 3, 2, 1, 3) + >>> unpack_tuple_from_iterable(Iter([3])) + (1, 2, 3, 1, 2, 1) + + >>> unpack_tuple_from_iterable([]) + (1, 2, 1, 2, 1) + >>> unpack_tuple_from_iterable(set([])) + (1, 2, 1, 2, 1) + >>> unpack_tuple_from_iterable([]) + (1, 2, 1, 2, 1) + >>> unpack_tuple_from_iterable(Iter([1, 2, 3])) + (1, 2, 1, 2, 3, 1, 2, 1) + """ + return (1, 2, *it, 1, *(*it, *it), *it, 2, 1, *it) + + +def unpack_tuple_keep_originals(a, b, c): + """ + >>> a = b = [1, 2] + >>> c = [3, 4] + >>> unpack_tuple_keep_originals(a, b, c) + (1, 2, 1, 2, 2, 3, 4) + >>> a + [1, 2] + >>> b + [1, 2] + >>> c + [3, 4] + + >>> a = b = (1, 2) + >>> c = (3, 4) + >>> unpack_tuple_keep_originals(a, b, c) + (1, 2, 1, 2, 2, 3, 4) + >>> a + (1, 2) + >>> b + (1, 2) + >>> c + (3, 4) + """ + return (*a, *b, 2, *c) + + +#### lists + + +@cython.test_fail_if_path_exists( + "//ListNode//ListNode", + "//MergedSequenceNode", +) +def unpack_list_literal(): + """ + >>> unpack_list_literal() + [1, 2, 4, 5] + """ + return [*[1, 2, *[4, 5]]] + + +def unpack_list_literal_mult(): + """ + >>> unpack_list_literal_mult() + [1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5] + """ + return [*([1, 2, *([4, 5] * 2)] * 3)] + + +@cython.test_fail_if_path_exists( + "//ListNode//ListNode", + "//MergedSequenceNode", +) +def unpack_list_literal_empty(): + """ + >>> unpack_list_literal_empty() + [] + """ + return [*[*[], *[]], *[], *[*[*[]]]] + + +def unpack_list_simple(it): + """ + >>> unpack_list_simple([]) + [] + >>> unpack_list_simple(set()) + [] + >>> unpack_list_simple(Iter()) + [] + + >>> unpack_list_simple([1]) + [1] + + >>> unpack_list_simple([2, 1]) + [2, 1] + >>> unpack_list_simple((2, 1)) + [2, 1] + >>> sorted(unpack_list_simple(set([2, 1]))) + [1, 2] + >>> unpack_list_simple(Iter([2, 1])) + [2, 1] + """ + return [*it] + + +def unpack_list_from_iterable(it): + """ + >>> unpack_list_from_iterable([1, 2, 3]) + [1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3] + >>> unpack_list_from_iterable((1, 2, 3)) + [1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3] + >>> sorted(unpack_list_from_iterable(set([1, 2, 3]))) + [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3] + + >>> unpack_list_from_iterable([1, 2]) + [1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2] + >>> sorted(unpack_list_from_iterable(set([1, 2]))) + [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2] + >>> unpack_list_from_iterable(Iter([1, 2])) + [1, 2, 1, 2, 1, 2, 1] + + >>> unpack_list_from_iterable([3]) + [1, 2, 3, 1, 3, 3, 3, 2, 1, 3] + >>> unpack_list_from_iterable(set([3])) + [1, 2, 3, 1, 3, 3, 3, 2, 1, 3] + >>> unpack_list_from_iterable(Iter([3])) + [1, 2, 3, 1, 2, 1] + + >>> unpack_list_from_iterable([]) + [1, 2, 1, 2, 1] + >>> unpack_list_from_iterable(set([])) + [1, 2, 1, 2, 1] + >>> unpack_list_from_iterable([]) + [1, 2, 1, 2, 1] + >>> unpack_list_from_iterable(Iter([1, 2, 3])) + [1, 2, 1, 2, 3, 1, 2, 1] + """ + return [1, 2, *it, 1, *[*it, *it], *it, 2, 1, *it] + + +def unpack_list_keep_originals(a, b, c): + """ + >>> a = b = [1, 2] + >>> c = [3, 4] + >>> unpack_list_keep_originals(a, b, c) + [1, 2, 1, 2, 2, 3, 4] + >>> a + [1, 2] + >>> b + [1, 2] + >>> c + [3, 4] + """ + return [*a, *b, 2, *c] + + +###### sets + + +@cython.test_fail_if_path_exists( + "//SetNode//SetNode", + "//MergedSequenceNode", +) +def unpack_set_literal(): + """ + >>> s = unpack_set_literal() + >>> s == set([1, 2, 4, 5]) or s + True + """ + return {*{1, 2, *{4, 5}}} + + +def unpack_set_simple(it): + """ + >>> s = unpack_set_simple([]) + >>> s == set([]) or s + True + + >>> s = unpack_set_simple(set()) + >>> s == set([]) or s + True + + >>> s = unpack_set_simple(Iter()) + >>> s == set([]) or s + True + + >>> s = unpack_set_simple([1]) + >>> s == set([1]) or s + True + + >>> s = unpack_set_simple([2, 1]) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_simple((2, 1)) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_simple(set([2, 1])) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_simple(Iter([2, 1])) + >>> s == set([1, 2]) or s + True + """ + return {*it} + + +def unpack_set_from_iterable(it): + """ + >>> s = unpack_set_from_iterable([1, 2, 3]) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable([1, 2]) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable(set([1, 2])) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable(Iter([1, 2])) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable([3]) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable(set([3])) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable(Iter([3])) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable([]) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable(set([])) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable([]) + >>> s == set([1, 2]) or s + True + + >>> s = unpack_set_from_iterable((1, 2, 3)) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable(set([1, 2, 3])) + >>> s == set([1, 2, 3]) or s + True + + >>> s = unpack_set_from_iterable(Iter([1, 2, 3])) + >>> s == set([1, 2, 3]) or s + True + """ + return {1, 2, *it, 1, *{*it, *it}, *it, 2, 1, *it, *it} + + +def unpack_set_keep_originals(a, b, c): + """ + >>> a = b = {1, 2} + >>> c = {3, 4} + >>> s = unpack_set_keep_originals(a, b, c) + >>> s == set([1, 2, 3, 4]) or s + True + >>> a == set([1, 2]) or a + True + >>> b == set([1, 2]) or b + True + >>> c == set([3, 4]) or c + True + """ + return {*a, *b, 2, *c} + + +#### dicts + + +@cython.test_fail_if_path_exists( + "//DictNode//DictNode", + "//MergedDictNode", +) +def unpack_dict_literal(): + """ + >>> d = unpack_dict_literal() + >>> d == dict(a=1, b=2, c=4, d=5) or d + True + """ + return {**{'a': 1, 'b': 2, **{'c': 4, 'd': 5}}} + + +@cython.test_fail_if_path_exists( + "//DictNode//DictNode", + "//MergedDictNode", +) +def unpack_dict_literal_empty(): + """ + >>> unpack_dict_literal_empty() + {} + """ + return {**{**{}, **{}}, **{}, **{**{**{}}}} + + +def unpack_dict_simple(it): + """ + >>> d = unpack_dict_simple({}) + >>> d == {} or d + True + + >>> d = unpack_dict_simple([]) + >>> d == {} or d + True + + >>> d = unpack_dict_simple(set()) + >>> d == {} or d + True + + >>> d = unpack_dict_simple(Iter()) + >>> d == {} or d + True + + >>> d = unpack_dict_simple(Map()) + >>> d == {} or d + True + + >>> d = unpack_dict_simple(dict(a=1)) + >>> d == dict(a=1) or d + True + + >>> d = unpack_dict_simple(dict(a=1, b=2)) + >>> d == dict(a=1, b=2) or d + True + + >>> d = unpack_dict_simple(Map(dict(a=1, b=2))) + >>> d == dict(a=1, b=2) or d + True + """ + return {**it} + + +def unpack_dict_from_iterable(it): + """ + >>> d = unpack_dict_from_iterable(dict(a=1, b=2, c=3)) + >>> d == dict(a=1, b=2, c=3) or d + True + + >>> d = unpack_dict_from_iterable(dict(a=1, b=2)) + >>> d == dict(a=1, b=2) or d + True + + >>> d = unpack_dict_from_iterable(Map(dict(a=1, b=2))) + >>> d == dict(a=1, b=2) or d + True + + >>> d = unpack_dict_from_iterable(dict(a=3)) + >>> d == dict(a=3, b=5) or d + True + + >>> d = unpack_dict_from_iterable(Map(dict(a=3))) + >>> d == dict(a=3, b=5) or d + True + + >>> d = unpack_dict_from_iterable({}) + >>> d == dict(a=4, b=5) or d + True + + >>> d = unpack_dict_from_iterable(Map()) + >>> d == dict(a=4, b=5) or d + True + + >>> d = unpack_dict_from_iterable(Iter()) + Traceback (most recent call last): + TypeError: 'Iter' object is not a mapping + + >>> d = unpack_dict_from_iterable([]) + Traceback (most recent call last): + TypeError: 'list' object is not a mapping + + >>> d = unpack_dict_from_iterable(dict(b=2, c=3)) + >>> d == dict(a=4, b=2, c=3) or d + True + + >>> d = unpack_dict_from_iterable(Map(dict(b=2, c=3))) + >>> d == dict(a=4, b=2, c=3) or d + True + + >>> d = unpack_dict_from_iterable(dict(a=2, c=3)) + >>> d == dict(a=2, b=5, c=3) or d + True + + >>> d = unpack_dict_from_iterable(Map(dict(a=2, c=3))) + >>> d == dict(a=2, b=5, c=3) or d + True + """ + return {'a': 2, 'b': 3, **it, 'a': 1, **{**it, **it}, **it, 'a': 4, 'b': 5, **it, **it} + + +def unpack_dict_keep_originals(a, b, c): + """ + >>> a = b = {1: 2} + >>> c = {2: 3, 4: 5} + >>> d = unpack_dict_keep_originals(a, b, c) + >>> d == {1: 2, 2: 3, 4: 5} or d + True + >>> a + {1: 2} + >>> b + {1: 2} + >>> c == {2: 3, 4: 5} or c + True + """ + return {**a, **b, 2: 4, **c} diff --git a/tests/run/pep448_test_extcall.pyx b/tests/run/pep448_test_extcall.pyx new file mode 100644 index 0000000000000000000000000000000000000000..a7bbeb1b0ff43da3288129f6e7da8d59f90772a7 --- /dev/null +++ b/tests/run/pep448_test_extcall.pyx @@ -0,0 +1,574 @@ +# mode: run +# tag: pep448 + +from __future__ import print_function + +import sys + +IS_PY3 = sys.version_info[0] >= 3 + +if IS_PY3: + __doc__ = """ +>>> def f(*, w): pass +>>> try: errors_call_no_args(f) +... except TypeError: pass +... else: print("FAILED!") + +>>> def f(*, a, b, c, d, e): pass +>>> try: errors_call_no_args(f) +... except TypeError: pass +... else: print("FAILED!") + +>>> def f(*, kw, b): pass +>>> try: errors_call_3args_2kwargs(f) +... except TypeError: pass +... else: print("FAILED!") + +>>> def f(a, b=2, *, kw): pass +>>> try: errors_call_3args_1kwarg(f) +... except TypeError: pass +... else: print("FAILED!") + +>>> def f(*, kw): pass +>>> try: errors_call_1arg_1kwarg(f) +... except TypeError: pass +... else: print("FAILED!") +""" + +# test for method/function calls. adapted from CPython's "test_extcall.py". + +def sortdict(d): + return '{%s}' % ', '.join(['%r: %r' % item for item in sorted(d.items())]) + +# We're going the use these types for extra testing + +try: + from collections import UserList, UserDict +except ImportError: + from UserList import UserList + from UserDict import UserDict + + +# We're defining four helper functions + +def e(a,b): + print(a, b) + +def f(*a, **k): + print(a, sortdict(k)) + +def g(x, *y, **z): + print(x, y, sortdict(z)) + +def h(j=1, a=2, h=3): + print(j, a, h) + + +# Argument list examples + +def call_f_positional(): + """ + >>> call_f_positional() + () {} + (1,) {} + (1, 2) {} + (1, 2, 3) {} + (1, 2, 3, 4, 5) {} + (1, 2, 3, 4, 5) {} + (1, 2, 3, 4, 5) {} + (1, 2, 3, 4, 5) {} + (1, 2, 3, 4, 5, 6, 7) {} + (1, 2, 3, 4, 5, 6, 7) {} + (1, 2, 3, 4, 5, 6, 7) {} + """ + f() + f(1) + f(1, 2) + f(1, 2, 3) + f(1, 2, 3, *(4, 5)) + f(1, 2, 3, *[4, 5]) + f(*[1, 2, 3], 4, 5) + f(1, 2, 3, *UserList([4, 5])) + f(1, 2, 3, *[4, 5], *[6, 7]) + f(1, *[2, 3], 4, *[5, 6], 7) + f(*UserList([1, 2]), *UserList([3, 4]), 5, *UserList([6, 7])) + + +# Here we add keyword arguments + +def call_f_kwargs(): + """ + >>> call_f_kwargs() + (1, 2, 3) {'a': 4, 'b': 5} + (1, 2, 3, 4, 5) {'a': 6, 'b': 7} + (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} + (1, 2, 3, 4, 5) {'a': 6, 'b': 7, 'c': 8} + (1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7} + (1, 2, 3) {'a': 4, 'b': 5} + (1, 2, 3, 4, 5) {'a': 6, 'b': 7} + (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} + (1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7} + """ + + f(1, 2, 3, **{'a':4, 'b':5}) + f(1, 2, 3, *[4, 5], **{'a':6, 'b':7}) + f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9}) + f(1, 2, 3, *[4, 5], **{'c': 8}, **{'a':6, 'b':7}) + f(1, 2, 3, *(4, 5), x=6, y=7, **{'a':8, 'b': 9}) + + f(1, 2, 3, **UserDict(a=4, b=5)) + f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7)) + f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9)) + f(1, 2, 3, *(4, 5), x=6, y=7, **UserDict(a=8, b=9)) + + +# Examples with invalid arguments (TypeErrors). We're also testing the function +# names in the exception messages. +# +# Verify clearing of SF bug #733667 + +def errors_f1(): + """ + >>> errors_f1() # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ...got multiple values for keyword argument 'a' + """ + f(1, 2, **{'a': -1, 'b': 5}, **{'a': 4, 'c': 6}) + + +def errors_f2(): + """ + >>> errors_f2() # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ...multiple values for keyword argument 'a' + """ + f(1, 2, **{'a': -1, 'b': 5}, a=4, c=6) + + +def errors_e1(): + """ + >>> try: errors_e1() + ... except TypeError: pass + ... else: print("FAILED!") + """ + e(c=4) + + +def errors_e2(): + """ + >>> try: errors_e2() + ... except TypeError: pass + ... else: print("FAILED!") + """ + e(a=1, b=2, c=4) + + +def errors_g1(): + """ + >>> errors_g1() + Traceback (most recent call last): + ... + TypeError: g() takes at least 1 positional argument (0 given) + + # TypeError: g() missing 1 required positional argument: 'x' + """ + g() + + +def errors_g2(): + """ + >>> errors_g2() + Traceback (most recent call last): + ... + TypeError: g() takes at least 1 positional argument (0 given) + + # TypeError: g() missing 1 required positional argument: 'x' + """ + g(*()) + + +def errors_g3(): + """ + >>> errors_g3() + Traceback (most recent call last): + ... + TypeError: g() takes at least 1 positional argument (0 given) + + # TypeError: g() missing 1 required positional argument: 'x' + """ + g(*(), **{}) + + +def call_g_positional(): + """ + >>> call_g_positional() + 1 () {} + 1 (2,) {} + 1 (2, 3) {} + 1 (2, 3, 4, 5) {} + """ + g(1) + g(1, 2) + g(1, 2, 3) + g(1, 2, 3, *(4, 5)) + + + +def call_nonseq_positional1(): + """ + >>> call_nonseq_positional1() + Traceback (most recent call last): + ... + TypeError: 'Nothing' object is not iterable + + # TypeError: g() argument after * must be a sequence, not Nothing + """ + class Nothing(object): pass + g(*Nothing()) + + +def call_nonseq_positional2(): + """ + >>> call_nonseq_positional2() + Traceback (most recent call last): + ... + TypeError: 'Nothing' object is not iterable + + # TypeError: g() argument after * must be a sequence, not Nothing + """ + class Nothing(object): + def __len__(self): return 5 + g(*Nothing()) + + +def call_seqlike_positional1(): + """ + >>> call_seqlike_positional1() + 0 (1, 2) {} + """ + class Nothing(object): + def __len__(self): return 5 + def __getitem__(self, i): + if i<3: return i + else: raise IndexError(i) + + g(*Nothing()) + + +def call_seqlike_positional2(): + """ + >>> call_seqlike_positional2() + 0 (1, 2, 3) {} + """ + class Nothing: + def __init__(self): self.c = 0 + def __iter__(self): return self + def __next__(self): + if self.c == 4: + raise StopIteration + c = self.c + self.c += 1 + return c + next = __next__ + + g(*Nothing()) + + +# Make sure that the function doesn't stomp the dictionary + +def call_kwargs_unmodified1(): + """ + >>> call_kwargs_unmodified1() + 1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4} + True + """ + d = {'a': 1, 'b': 2, 'c': 3} + d2 = d.copy() + g(1, d=4, **d) + return d == d2 + + +# What about willful misconduct? + +def call_kwargs_unmodified2(): + """ + >>> call_kwargs_unmodified2() + {} + """ + def saboteur(**kw): + kw['x'] = 'm' + return kw + + d = {} + kw = saboteur(a=1, **d) + return d + + +def errors_args_kwargs_overlap(): + """ + >>> errors_args_kwargs_overlap() # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ...got multiple values for... argument 'x' + """ + g(1, 2, 3, **{'x': 4, 'y': 5}) + + +def errors_non_string_kwarg(): + """ + >>> errors_non_string_kwarg() # doctest: +ELLIPSIS + Traceback (most recent call last): + TypeError: ...keywords must be strings + """ + f(**{1:2}) + + +def errors_unexpected_kwarg(): + """ + >>> errors_unexpected_kwarg() + Traceback (most recent call last): + ... + TypeError: h() got an unexpected keyword argument 'e' + """ + h(**{'e': 2}) + + +def errors_call_nonseq(): + """ + >>> try: errors_call_nonseq() + ... except TypeError: pass + ... else: print("FAILED!") + """ + h(*h) + + +def errors_call_builtin_nonseq(): + """ + >>> try: errors_call_builtin_nonseq() + ... except TypeError: pass + ... else: print("FAILED!") + """ + dir(*h) + + +def errors_call_none_nonseq(): + """ + >>> try: errors_call_none_nonseq() + ... except TypeError: pass + ... else: print("FAILED!") + """ + None(*h) + + +def errors_call_nonmapping_kwargs(): + """ + >>> try: errors_call_nonmapping_kwargs() + ... except TypeError: pass + ... else: print("FAILED!") + """ + h(**h) + + +def errors_call_builtin_nonmapping_kwargs(): + """ + >>> try: errors_call_builtin_nonmapping_kwargs() + ... except TypeError: pass + ... else: print("FAILED!") + """ + dir(**h) + + +def errors_call_none_nonmapping_kwargs(): + """ + >>> try: errors_call_none_nonmapping_kwargs() + ... except TypeError: pass + ... else: print("FAILED!") + """ + None(**h) + + +''' # compile time error in Cython +def errors_call_builtin_duplicate_kwarg(): + """ + >>> errors_call_builtin_duplicate_kwarg() # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ...got multiple values for keyword argument 'b' + """ + dir(b=1, **{'b': 1}) +''' + + +# Another helper function + +def f2(*a, **b): + return a, b + + +def call_many_kwargs(): + """ + call_many_kwargs() + (3, 512, True) + """ + d = {} + for i in range(512): + key = 'k%d' % i + d[key] = i + a, b = f2(1, *(2,3), **d) + return len(a), len(b), b == d + + +def call_method(Foo): + """ + >>> class Foo(object): + ... def method(self, arg1, arg2): + ... print(arg1+arg2) + + >>> call_method(Foo) + 3 + 3 + 5 + 5 + """ + x = Foo() + Foo.method(*(x, 1, 2)) + Foo.method(x, *(1, 2)) + if sys.version_info[0] >= 3: + Foo.method(*(1, 2, 3)) + Foo.method(1, *[2, 3]) + else: + print(5) + print(5) + + +# A PyCFunction that takes only positional parameters should allow an +# empty keyword dictionary to pass without a complaint, but raise a +# TypeError if te dictionary is not empty + +def call_builtin_empty_dict(): + """ + >>> call_builtin_empty_dict() + """ + silence = id(1, *{}) + silence = id(1, **{}) + + +def call_builtin_nonempty_dict(): + """ + >>> call_builtin_nonempty_dict() + Traceback (most recent call last): + ... + TypeError: id() takes no keyword arguments + """ + return id(1, **{'foo': 1}) + + +''' Cython: currently just passes empty kwargs into f() while CPython keeps the content + +# A corner case of keyword dictionary items being deleted during +# the function call setup. See <http://bugs.python.org/issue2016>. + +def call_kwargs_modified_while_building(): + """ + >>> call_kwargs_modified_while_building() + 1 2 + """ + class Name(str): + def __eq__(self, other): + try: + del x[self] + except KeyError: + pass + return str.__eq__(self, other) + def __hash__(self): + return str.__hash__(self) + + x = {Name("a"):1, Name("b"):2} + def f(a, b): + print(a,b) + f(**x) +''' + + +# Too many arguments: + +def errors_call_one_arg(f): + """ + >>> def f(): pass + >>> try: errors_call_one_arg(f) + ... except TypeError: pass + ... else: print("FAILED!") + """ + f(1) + +def errors_call_2args(f): + """ + >>> def f(a): pass + >>> try: errors_call_2args(f) + ... except TypeError: pass + ... else: print("FAILED!") + """ + f(1, 2) + +def errors_call_3args(f): + """ + >>> def f(a, b=1): pass + >>> try: errors_call_3args(f) + ... except TypeError: pass + ... else: print("FAILED!") + """ + f(1, 2, 3) + + +def errors_call_1arg_1kwarg(f): + # Py3 only + f(1, kw=3) + + +def errors_call_3args_2kwargs(f): + # Py3 only + f(1, 2, 3, b=3, kw=3) + + +def errors_call_3args_1kwarg(f): + # Py3 only + f(2, 3, 4, kw=4) + + +# Too few and missing arguments: + +def errors_call_no_args(f): + """ + >>> def f(a): pass + >>> try: errors_call_no_args(f) + ... except TypeError: pass + ... else: print("FAILED!") + + >>> def f(a, b): pass + >>> try: errors_call_no_args(f) + ... except TypeError: pass + ... else: print("FAILED!") + + >>> def f(a, b, c): pass + >>> try: errors_call_no_args(f) + ... except TypeError: pass + ... else: print("FAILED!") + + >>> def f(a, b, c, d, e): pass + >>> try: errors_call_no_args(f) + ... except TypeError: pass + ... else: print("FAILED!") + """ + f() + + +def errors_call_one_missing_kwarg(f): + """ + >>> def f(a, b=4, c=5, d=5): pass + >>> try: errors_call_one_missing_kwarg(f) + ... except TypeError: pass + ... else: print("FAILED!") + """ + f(c=12, b=9) diff --git a/tests/run/powop.pyx b/tests/run/powop.pyx index 88da79a8b2f46ccf938fc208c3baab679b6486ef..966336c2a29a2e82bf253181c798e5345f69b5f3 100644 --- a/tests/run/powop.pyx +++ b/tests/run/powop.pyx @@ -105,8 +105,16 @@ def optimised_pow2(n): 1024 >>> optimised_pow2(30) 1073741824 + >>> print(repr(optimised_pow2(31)).rstrip('L')) + 2147483648 >>> print(repr(optimised_pow2(32)).rstrip('L')) 4294967296 + >>> print(repr(optimised_pow2(60)).rstrip('L')) + 1152921504606846976 + >>> print(repr(optimised_pow2(63)).rstrip('L')) + 9223372036854775808 + >>> print(repr(optimised_pow2(64)).rstrip('L')) + 18446744073709551616 >>> print(repr(optimised_pow2(100)).rstrip('L')) 1267650600228229401496703205376 >>> optimised_pow2(30000) == 2 ** 30000 diff --git a/tests/run/pure_py.py b/tests/run/pure_py.py index 5f436f4089a5d82fa6aea390637755312d2f99d4..2fa801744f302d209560c6fc09e3f43eeee1878f 100644 --- a/tests/run/pure_py.py +++ b/tests/run/pure_py.py @@ -5,6 +5,7 @@ is_compiled = cython.compiled NULL = 5 _NULL = NULL + def test_sizeof(): """ >>> test_sizeof() @@ -24,6 +25,7 @@ def test_sizeof(): else: print(cython.sizeof(cython.char) == 1) + def test_declare(n): """ >>> test_declare(100) @@ -43,6 +45,7 @@ def test_declare(n): ptr = cython.declare(cython.p_int, cython.address(y)) return y, ptr[0] + @cython.locals(x=cython.double, n=cython.int) def test_cast(x): """ @@ -52,6 +55,7 @@ def test_cast(x): n = cython.cast(cython.int, x) return n + @cython.locals(x=cython.int, y=cython.p_int) def test_address(x): """ @@ -61,6 +65,30 @@ def test_address(x): y = cython.address(x) return y[0] + +@cython.wraparound(False) +def test_wraparound(x): + """ + >>> test_wraparound([1, 2, 3]) + [1, 2, 1] + """ + with cython.wraparound(True): + x[-1] = x[0] + return x + + +@cython.boundscheck(False) +def test_boundscheck(x): + """ + >>> test_boundscheck([1, 2, 3]) + 3 + >>> try: test_boundscheck([1, 2]) + ... except IndexError: pass + """ + with cython.boundscheck(True): + return x[2] + + ## CURRENTLY BROKEN - FIXME!! ## Is this test make sense? Implicit conversion in pure Python?? @@ -74,6 +102,7 @@ def test_address(x): ## y = x ## return y + def test_with_nogil(nogil): """ >>> raised = [] diff --git a/tests/run/py_ucs4_type.pyx b/tests/run/py_ucs4_type.pyx index ce495f455892af3fdb9e6379dca50d09287c4a5c..433e1315d1d16827efa66d270c077ec85147a6db 100644 --- a/tests/run/py_ucs4_type.pyx +++ b/tests/run/py_ucs4_type.pyx @@ -5,6 +5,10 @@ cimport cython cdef Py_UCS4 char_ASCII = u'A' cdef Py_UCS4 char_KLINGON = u'\uF8D2' +u_A = char_ASCII +u_KLINGON = char_KLINGON + + def compare_ASCII(): """ >>> compare_ASCII() @@ -86,6 +90,19 @@ def unicode_ordinal(Py_UCS4 i): """ return i + +def ord_py_ucs4(Py_UCS4 x): + """ + >>> ord_py_ucs4(u0) + 0 + >>> ord_py_ucs4(u_A) + 65 + >>> ord_py_ucs4(u_KLINGON) + 63698 + """ + return ord(x) + + @cython.test_assert_path_exists('//PythonCapiCallNode') @cython.test_fail_if_path_exists('//SimpleCallNode') def unicode_type_methods(Py_UCS4 uchar): diff --git a/tests/run/py_unicode_type.pyx b/tests/run/py_unicode_type.pyx index 4bbf6bc09ff014830df2e7a6713c921fce573464..78a5e4a51adbad8ecdd8af2240254632f5f5024c 100644 --- a/tests/run/py_unicode_type.pyx +++ b/tests/run/py_unicode_type.pyx @@ -5,6 +5,10 @@ cimport cython cdef Py_UNICODE char_ASCII = u'A' cdef Py_UNICODE char_KLINGON = u'\uF8D2' +u_A = char_ASCII +u_KLINGON = char_KLINGON + + def compare_ASCII(): """ >>> compare_ASCII() @@ -78,6 +82,19 @@ def unicode_ordinal(Py_UNICODE i): """ return i + +def ord_pyunicode(Py_UNICODE x): + """ + >>> ord_pyunicode(u0) + 0 + >>> ord_pyunicode(u_A) + 65 + >>> ord_pyunicode(u_KLINGON) + 63698 + """ + return ord(x) + + @cython.test_assert_path_exists('//PythonCapiCallNode') @cython.test_fail_if_path_exists('//SimpleCallNode') def unicode_type_methods(Py_UNICODE uchar): diff --git a/tests/run/pyintop.pyx b/tests/run/pyintop.pyx index f222d1c8a65318640773e21f4db52015c34abeb0..5c44683b83e1a09f8917e75c6a40ba4299bfb505 100644 --- a/tests/run/pyintop.pyx +++ b/tests/run/pyintop.pyx @@ -1,47 +1,152 @@ -def f(obj1, obj2, obj3): +# mode: run + + +def bigint(x): + # avoid 'L' postfix in Py2.x + print(str(x).rstrip('L')) + + +def or_obj(obj2, obj3): """ - >>> f(1,2,3) + >>> or_obj(2, 3) 3 """ obj1 = obj2 | obj3 return obj1 -def g(obj1, obj2, obj3): + +def or_int(obj2): + """ + >>> or_int(1) + 17 + >>> or_int(16) + 16 + """ + obj1 = obj2 | 0x10 + return obj1 + + +def xor_obj(obj2, obj3): """ - >>> g(1,2,3) + >>> xor_obj(2, 3) 1 """ obj1 = obj2 ^ obj3 return obj1 -def h(obj1, obj2, obj3): + +def xor_int(obj2): + """ + >>> xor_int(2) + 18 + >>> xor_int(16) + 0 + """ + obj1 = obj2 ^ 0x10 + return obj1 + + +def and_obj(obj2, obj3): """ - >>> h(1,2,3) + >>> and_obj(2, 3) 2 """ obj1 = obj2 & obj3 return obj1 -def j(obj1, obj2, obj3): + +def and_int(obj2): """ - >>> j(1,2,3) + >>> and_int(1) + 0 + >>> and_int(18) + 16 + """ + obj1 = obj2 & 0x10 + return obj1 + + +def lshift_obj(obj2, obj3): + """ + >>> lshift_obj(2, 3) 16 """ obj1 = obj2 << obj3 return obj1 -def k(obj1, obj2, obj3): + +def rshift_obj(obj2, obj3): """ - >>> k(1,2,3) + >>> rshift_obj(2, 3) 0 """ obj1 = obj2 >> obj3 return obj1 -def l(obj1, obj2, obj3): + +def rshift_int(obj2): + """ + >>> rshift_int(2) + 0 + + >>> rshift_int(27) + 3 + >>> (-27) >> 3 + -4 + >>> rshift_int(-27) + -4 + + >>> rshift_int(32) + 4 + >>> (-32) >> 3 + -4 + >>> rshift_int(-32) + -4 + + >>> (2**28) >> 3 + 33554432 + >>> rshift_int(2**28) + 33554432 + >>> (-2**28) >> 3 + -33554432 + >>> rshift_int(-2**28) + -33554432 + + >>> (2**30) >> 3 + 134217728 + >>> rshift_int(2**30) + 134217728 + >>> rshift_int(-2**30) + -134217728 + + >>> bigint((2**60) >> 3) + 144115188075855872 + >>> bigint(rshift_int(2**60)) + 144115188075855872 + >>> bigint(rshift_int(-2**60)) + -144115188075855872 """ - >>> l(1,2,3) + obj1 = obj2 >> 3 + return obj1 + + +def mixed_obj(obj2, obj3): + """ + >>> mixed_obj(2, 3) 16 """ obj1 = obj2 << obj3 | obj2 >> obj3 return obj1 + + +def mixed_int(obj2): + """ + >>> mixed_int(2) + 18 + >>> mixed_int(16) + 0 + >>> mixed_int(17) + 1 + """ + obj1 = (obj2 ^ 0x10) | (obj2 & 0x01) + return obj1 diff --git a/tests/run/relative_cimport.srctree b/tests/run/relative_cimport.srctree index 09bbc897b88a69b0dc718b9a0c01280ed7a51c58..c547a86bee90073f20e1ac0bcd4b36ef44486931 100644 --- a/tests/run/relative_cimport.srctree +++ b/tests/run/relative_cimport.srctree @@ -37,8 +37,10 @@ cdef class test_pxd: from . cimport a from .a cimport test_pxd +cimport a as implicitly_relative_a assert a.test_pxd is test_pxd +assert implicitly_relative_a.test_pxd is test_pxd def test(): cdef test_pxd obj = test_pxd() diff --git a/tests/run/set.pyx b/tests/run/set.pyx index abd313790f5ca3f5bcfb73c66eba5c73cf71830e..5aa87a09aafc9b6398a2917b59e08e7efe470bde 100644 --- a/tests/run/set.pyx +++ b/tests/run/set.pyx @@ -1,16 +1,10 @@ -# Py2.3 doesn't have the 'set' builtin type, but Cython does :) -_set = set -_frozenset = frozenset - cimport cython -import sys - def cython_set(): """ - >>> cython_set() is _set + >>> cython_set() is set True """ assert set is cython.set @@ -19,7 +13,7 @@ def cython_set(): def cython_frozenset(): """ - >>> cython_frozenset() is _frozenset + >>> cython_frozenset() is frozenset True """ assert frozenset is cython.frozenset @@ -28,7 +22,7 @@ def cython_frozenset(): def cython_set_override(): """ - >>> cython_set_override() is _set + >>> cython_set_override() is set True """ set = 1 @@ -37,7 +31,7 @@ def cython_set_override(): def cython_frozenset_override(): """ - >>> cython_frozenset_override() is _frozenset + >>> cython_frozenset_override() is frozenset True """ frozenset = 1 @@ -46,7 +40,7 @@ def cython_frozenset_override(): def test_set_literal(): """ - >>> type(test_set_literal()) is _set + >>> type(test_set_literal()) is set True >>> sorted(test_set_literal()) ['a', 'b', 1] @@ -57,7 +51,7 @@ def test_set_literal(): def test_set_add(): """ - >>> type(test_set_add()) is _set + >>> type(test_set_add()) is set True >>> sorted(test_set_add()) ['a', 1, (1, 2)] @@ -71,9 +65,55 @@ def test_set_add(): return s1 +def test_set_update(v=None): + """ + >>> type(test_set_update()) is set + True + >>> sorted(test_set_update()) + ['a', 'b', 'c', 1, 2, (1, 2)] + >>> sorted(test_set_update([])) + ['a', 'b', 'c', 1, 2, (1, 2)] + >>> try: test_set_update(object()) + ... except TypeError: pass + ... else: print("NOT RAISED!") + """ + cdef set s1 + s1 = set([1, (1, 2)]) + s1.update((1,)) + s1.update('abc') + s1.update(set([1])) + s1.update(frozenset((1,2))) + if v is not None: + s1.update(v) + return s1 + + +def test_object_update(v=None): + """ + >>> type(test_object_update()) is set + True + >>> sorted(test_object_update()) + ['a', 'b', 'c', 1, 2, (1, 2)] + >>> sorted(test_object_update([])) + ['a', 'b', 'c', 1, 2, (1, 2)] + >>> try: test_object_update(object()) + ... except TypeError: pass + ... else: print("NOT RAISED!") + """ + cdef object s1 + s1 = set([1, (1, 2)]) + s1.update((1,)) + s1.update('abc') + s1.update(set([1])) + s1.update(frozenset((1,2))) + if v is not None: + s1.update(v) + return s1 + + def test_set_clear(): """ - >>> type(test_set_clear()) is _set + >>> type(test_set_clear()) is set True >>> list(test_set_clear()) [] @@ -96,7 +136,7 @@ def test_set_clear_None(): def test_set_list_comp(): """ - >>> type(test_set_list_comp()) is _set + >>> type(test_set_list_comp()) is set True >>> sorted(test_set_list_comp()) [0, 1, 2] @@ -108,7 +148,7 @@ def test_set_list_comp(): def test_frozenset_list_comp(): """ - >>> type(test_frozenset_list_comp()) is _frozenset + >>> type(test_frozenset_list_comp()) is frozenset True >>> sorted(test_frozenset_list_comp()) [0, 1, 2] @@ -120,7 +160,7 @@ def test_frozenset_list_comp(): def test_set_pop(): """ - >>> type(test_set_pop()) is _set + >>> type(test_set_pop()) is set True >>> list(test_set_pop()) [] @@ -135,7 +175,7 @@ def test_set_pop(): @cython.test_fail_if_path_exists("//SimpleCallNode//NameNode") def test_object_pop(s): """ - >>> s = _set([2]) + >>> s = set([2]) >>> test_object_pop(s) 2 >>> list(s) @@ -162,7 +202,7 @@ def test_noop_pop_exception(): def test_set_discard(): """ - >>> type(test_set_discard()) is _set + >>> type(test_set_discard()) is set True >>> sorted(test_set_discard()) ['12', 233] @@ -242,7 +282,7 @@ def test_frozenset_sideeffect_unhashable_failure(): def test_set_of_list(): """ >>> s = test_set_of_list() - >>> isinstance(s, _set) + >>> isinstance(s, set) True >>> sorted(s) [1, 2, 3] @@ -255,7 +295,7 @@ def test_set_of_list(): def test_frozenset_of_list(): """ >>> s = test_frozenset_of_list() - >>> isinstance(s, _frozenset) + >>> isinstance(s, frozenset) True >>> sorted(s) [1, 2, 3] @@ -268,7 +308,7 @@ def test_frozenset_of_list(): def test_set_of_tuple(): """ >>> s = test_set_of_tuple() - >>> isinstance(s, _set) + >>> isinstance(s, set) True >>> sorted(s) [1, 2, 3] @@ -281,7 +321,7 @@ def test_set_of_tuple(): def test_frozenset_of_tuple(): """ >>> s = test_frozenset_of_tuple() - >>> isinstance(s, _frozenset) + >>> isinstance(s, frozenset) True >>> sorted(s) [1, 2, 3] @@ -297,7 +337,7 @@ def test_frozenset_of_tuple(): def test_set_of_iterable(x): """ >>> s = test_set_of_iterable([1, 2, 3]) - >>> isinstance(s, _set) + >>> isinstance(s, set) True >>> sorted(s) [1, 2, 3] @@ -313,13 +353,13 @@ def test_set_of_iterable(x): def test_frozenset_of_iterable(x): """ >>> s = test_frozenset_of_iterable([1, 2, 3]) - >>> isinstance(s, _frozenset) + >>> isinstance(s, frozenset) True >>> sorted(s) [1, 2, 3] - >>> s = test_frozenset_of_iterable(_frozenset([1, 2, 3])) - >>> isinstance(s, _frozenset) + >>> s = test_frozenset_of_iterable(frozenset([1, 2, 3])) + >>> isinstance(s, frozenset) True >>> sorted(s) [1, 2, 3] @@ -335,11 +375,11 @@ def test_frozenset_of_iterable(x): def test_empty_frozenset(): """ >>> s = test_empty_frozenset() - >>> isinstance(s, _frozenset) + >>> isinstance(s, frozenset) True >>> len(s) 0 - >>> sys.version_info < (2,5) or s is frozenset() # singleton! + >>> s is frozenset() # singleton! True """ return frozenset() @@ -360,7 +400,7 @@ def test_singleton_empty_frozenset(): frozenset(), frozenset([]), frozenset(()), frozenset(''), frozenset(range(0)), frozenset(frozenset()), frozenset(f), f] - return len(set(map(id, efs))) if sys.version_info >= (2,5) else 1 + return len(set(map(id, efs))) def sorted(it): diff --git a/tests/run/struct_conversion.pyx b/tests/run/struct_conversion.pyx index 9a7c7a25fb3d22965864d833d49f1d068432cdd0..26bd62686a92becd899977ca80a1a0d185bab0b1 100644 --- a/tests/run/struct_conversion.pyx +++ b/tests/run/struct_conversion.pyx @@ -3,7 +3,7 @@ cdef struct Point: double y int color -def test_constructor(x, y, color): +def test_constructor(x, y, int color): """ >>> sorted(test_constructor(1,2,255).items()) [('color', 255), ('x', 1.0), ('y', 2.0)] @@ -13,6 +13,17 @@ def test_constructor(x, y, color): cdef Point p = Point(x, y, color) return p + +def return_constructor(x, y, int color): + """ + >>> sorted(return_constructor(1,2,255).items()) + [('color', 255), ('x', 1.0), ('y', 2.0)] + >>> try: return_constructor(1, None, 255) + ... except TypeError: pass + """ + return Point(x, y, color) + + def test_constructor_kwds(x, y, color): """ >>> sorted(test_constructor_kwds(1.25, 2.5, 128).items()) @@ -25,6 +36,19 @@ def test_constructor_kwds(x, y, color): cdef Point p = Point(x=x, y=y, color=color) return p + +def return_constructor_kwds(double x, y, color): + """ + >>> sorted(return_constructor_kwds(1.25, 2.5, 128).items()) + [('color', 128), ('x', 1.25), ('y', 2.5)] + >>> return_constructor_kwds(1.25, 2.5, None) + Traceback (most recent call last): + ... + TypeError: an integer is required + """ + return Point(x=x, y=y, color=color) + + def test_dict_construction(x, y, color): """ >>> sorted(test_dict_construction(4, 5, 64).items()) diff --git a/tests/run/subop.pyx b/tests/run/subop.pyx index a7e7cbe840accc53017789e9015170927b9365ea..547a0c2c3e9b6e7e2e7bad831297a2d38e7d2ffb 100644 --- a/tests/run/subop.pyx +++ b/tests/run/subop.pyx @@ -1,6 +1,10 @@ cimport cython +def bigint(x): + print(str(x).rstrip('L')) + + def mixed_test(): """ >>> mixed_test() @@ -44,6 +48,10 @@ def sub_x_1(x): 0 >>> sub_x_1(-1) -2 + >>> bigint(2**50 - 1) + 1125899906842623 + >>> bigint(sub_x_1(2**50)) + 1125899906842623 >>> sub_x_1(1.5) 0.5 >>> sub_x_1(-1.5) @@ -63,6 +71,12 @@ def sub_x_1f(x): 0.0 >>> sub_x_1f(-1) -2.0 + >>> 2**52 - 1.0 + 4503599627370495.0 + >>> sub_x_1f(2**52) + 4503599627370495.0 + >>> sub_x_1f(2**60) == 2**60 - 1.0 or sub_x_1f(2**60) + True >>> sub_x_1f(1.5) 0.5 >>> sub_x_1f(-1.5) @@ -82,6 +96,10 @@ def sub_x_large(x): -1073741823 >>> sub_x_large(-1) -1073741825 + >>> bigint(2**50 - 2**30) + 1125898833100800 + >>> bigint(sub_x_large(2**50)) + 1125898833100800 >>> sub_x_large(2.0**30) 0.0 >>> sub_x_large(2.0**30 + 1) @@ -107,6 +125,10 @@ def sub_1_x(x): 2 >>> sub_1_x(1) 0 + >>> bigint(1 - 2**50) + -1125899906842623 + >>> bigint(sub_1_x(2**50)) + -1125899906842623 >>> sub_1_x(1.5) -0.5 >>> sub_1_x(-1.5) @@ -126,6 +148,12 @@ def sub_1f_x(x): 2.0 >>> sub_1f_x(1) 0.0 + >>> 1.0 - 2**52 + -4503599627370495.0 + >>> sub_1f_x(2**52) + -4503599627370495.0 + >>> sub_1f_x(2**60) == 1.0 - 2**60 or sub_1f_x(2**60) + True >>> sub_1f_x(1.5) -0.5 >>> sub_1f_x(-1.5) diff --git a/tests/run/unpack.pyx b/tests/run/unpack.pyx index 9c4a32d0d7060368bf812fb79677c0d8c7e5e0b2..becb6fbe876a1f2b2d9bcd9030bef9673ef23ce3 100644 --- a/tests/run/unpack.pyx +++ b/tests/run/unpack.pyx @@ -3,12 +3,12 @@ import cython -_set = set def _it(N): for i in range(N): yield i + cdef class ItCount(object): cdef object values cdef readonly count @@ -89,9 +89,9 @@ def unpack_partial(it): """ >>> it = _it(2) >>> a = b = c = 0 - >>> a,b,c = it - Traceback (most recent call last): - ValueError: need more than 2 values to unpack + >>> try: a,b,c = it + ... except ValueError: pass + ... else: print("DID NOT FAIL!") >>> a, b, c (0, 0, 0) >>> unpack_partial([1,2]) @@ -103,9 +103,9 @@ def unpack_partial(it): >>> it = ItCount([1,2]) >>> a = b = c = 0 - >>> a,b,c = it - Traceback (most recent call last): - ValueError: need more than 2 values to unpack + >>> try: a,b,c = it + ... except ValueError: pass + ... else: print("DID NOT FAIL!") >>> a, b, c (0, 0, 0) >>> it.count @@ -153,7 +153,7 @@ def unpack_partial_typed(it): (0, 0, 0) >>> unpack_partial_typed((1, 'abc', 3)) (0, 0, 0) - >>> unpack_partial_typed(_set([1, 'abc', 3])) + >>> unpack_partial_typed(set([1, 'abc', 3])) (0, 0, 0) >>> it = ItCount([1, 'abc', 3]) @@ -222,10 +222,10 @@ def failure_too_many(it): Traceback (most recent call last): ValueError: too many values to unpack (expected 3) - >>> a,b,c = _set([1,2,3,4]) # doctest: +ELLIPSIS + >>> a,b,c = set([1,2,3,4]) # doctest: +ELLIPSIS Traceback (most recent call last): ValueError: too many values to unpack... - >>> failure_too_many(_set([1,2,3,4])) + >>> failure_too_many(set([1,2,3,4])) Traceback (most recent call last): ValueError: too many values to unpack (expected 3) @@ -239,6 +239,7 @@ def failure_too_many(it): a,b,c = it return a,b,c + def failure_too_few(it): """ >>> try: a,b,c = [1,2] @@ -253,16 +254,16 @@ def failure_too_few(it): Traceback (most recent call last): ValueError: need more than 2 values to unpack - >>> a,b,c = _set([1,2]) - Traceback (most recent call last): - ValueError: need more than 2 values to unpack - >>> failure_too_few(_set([1,2])) + >>> try: a,b,c = set([1,2]) + ... except ValueError: pass + ... else: print("DID NOT FAIL!") + >>> failure_too_few(set([1,2])) Traceback (most recent call last): ValueError: need more than 2 values to unpack - >>> a,b,c = _it(2) - Traceback (most recent call last): - ValueError: need more than 2 values to unpack + >>> try: a,b,c = _it(2) + ... except ValueError: pass + ... else: print("DID NOT FAIL!") >>> failure_too_few(_it(2)) Traceback (most recent call last): ValueError: need more than 2 values to unpack @@ -270,6 +271,7 @@ def failure_too_few(it): a,b,c = it return a,b,c + def _it_failure(N): for i in range(N): yield i diff --git a/tests/run/yield_from_pep380.pyx b/tests/run/yield_from_pep380.pyx index 91045cf558fb54e36e5ce21c862e1e723ae11b10..1bc4d972aacd37d949f7cbc8713f5ce24834c794 100644 --- a/tests/run/yield_from_pep380.pyx +++ b/tests/run/yield_from_pep380.pyx @@ -355,6 +355,7 @@ def __test_value_attribute_of_StopIteration_exception(): pex(e) return trace + def test_exception_value_crash(): """ >>> test_exception_value_crash() @@ -369,6 +370,50 @@ def test_exception_value_crash(): return [42] return list(g1()) + +def test_return_none(): + """ + >>> test_return_none() + ['g2'] + """ + # There used to be a refcount error in CPython when the return value + # stored in the StopIteration has a refcount of 1. + def g1(): + yield from g2() + def g2(): + yield "g2" + return None + return list(g1()) + + +def test_finally_return_none(raise_exc=None): + """ + >>> gen = test_finally_return_none() + >>> next(gen) + 'g2' + >>> next(gen) + Traceback (most recent call last): + StopIteration + + >>> gen = test_finally_return_none() + >>> next(gen) + 'g2' + >>> try: gen.throw(ValueError()) + ... except StopIteration: pass + ... else: print("FAILED") + """ + # There used to be a refcount error in CPython when the return value + # stored in the StopIteration has a refcount of 1. + def g1(): + yield from g2() + def g2(): + try: + yield "g2" + finally: + return None + return g1() + + def test_generator_return_value(): """ >>> _lines(test_generator_return_value()) diff --git a/tests/run/yield_from_py33.pyx b/tests/run/yield_from_py33.pyx new file mode 100644 index 0000000000000000000000000000000000000000..c219cf0deb6e654782adebced2b085de92e3e093 --- /dev/null +++ b/tests/run/yield_from_py33.pyx @@ -0,0 +1,22 @@ +# mode: run +# tag: generator + +def yield_from_gen(values): + """ + >>> def yf(x): yield from x + >>> list(yf(yield_from_gen([1, 2, 3, 4]))) + [1, 2, 3, 4] + """ + for value in values: + yield value + + +def yield_from_gen_return(values): + """ + >>> def yf(x): yield from x + >>> list(yf(yield_from_gen_return([1, 2, 3, 4]))) + [1, 2, 3, 4] + """ + for value in values: + yield value + return 5