diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2018-09-29 19:08:13 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2018-09-29 19:08:13 +0200 |
commit | c9eda12b4806fba3931f7b791be7a672c92d9ca0 (patch) | |
tree | 9d6416c9dc1fe67b09b5dea5a5a26a581456daa6 | |
parent | f75b8e8fc8ea3842cbf3ae22716fc5c3b4b96102 (diff) | |
download | cython-c9eda12b4806fba3931f7b791be7a672c92d9ca0.tar.gz |
Rename the options of the "check_size" feature to make them more obvious: "warn" warns, "error" fails, and "extend" silently allows extending.
Closes #2627.
-rw-r--r-- | Cython/Compiler/ModuleNode.py | 8 | ||||
-rw-r--r-- | Cython/Compiler/Nodes.py | 4 | ||||
-rw-r--r-- | Cython/Compiler/Parsing.py | 14 | ||||
-rw-r--r-- | Cython/Compiler/PyrexTypes.py | 6 | ||||
-rw-r--r-- | Cython/Compiler/Symtab.py | 2 | ||||
-rw-r--r-- | Cython/Includes/numpy/__init__.pxd | 4 | ||||
-rw-r--r-- | Cython/Utility/ImportExport.c | 10 | ||||
-rw-r--r-- | docs/src/userguide/extension_types.rst | 17 | ||||
-rw-r--r-- | tests/run/check_size.srctree | 51 |
9 files changed, 56 insertions, 60 deletions
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index fd65855c2..c86a12263 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3062,11 +3062,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): # check_size if not type.is_external or type.is_subclassed: cs = 0 - elif type.check_size == 'min': - cs = 1 - elif type.check_size == True: + elif type.check_size == 'error': cs = 0 - elif type.check_size == False: + elif type.check_size == 'warn': + cs = 1 + elif type.check_size == 'extend': cs = 2 else: raise RuntimeError("invalid value for check_size '%s' when compiling %s.%s" % ( diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index dad9b70cd..48287b3fa 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -4630,7 +4630,7 @@ class CClassDefNode(ClassDefNode): # bases TupleNode Base class(es) # objstruct_name string or None Specified C name of object struct # typeobj_name string or None Specified C name of type object - # check_size 'min' or boolean What to do if tp_basicsize does not match + # check_size 'warn', 'error', 'extend' What to do if tp_basicsize does not match # in_pxd boolean Is in a .pxd file # decorators [DecoratorNode] list of decorators or None # doc string or None @@ -4647,7 +4647,7 @@ class CClassDefNode(ClassDefNode): api = False objstruct_name = None typeobj_name = None - check_size = 'min' + check_size = None decorators = None shadow = False diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 49bbba0c7..79dd1e04c 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -3512,8 +3512,6 @@ def p_c_class_definition(s, pos, ctx): error(pos, "Type object name specification required for 'api' C class") else: error(pos, "Invalid class visibility '%s'" % ctx.visibility) - if check_size is None: - check_size = 'min' # TODO: move into 'CClassDefNode' return Nodes.CClassDefNode(pos, visibility = ctx.visibility, typedef_flag = ctx.typedef_flag, @@ -3547,18 +3545,12 @@ def p_c_class_options(s): elif s.systring == 'check_size': s.next() check_size = p_ident(s) - if check_size == 'False': - check_size = False - elif check_size == 'True': - check_size = True - elif check_size == 'min': - pass - else: - s.error('Expected False, True, or min, not %r' % check_size) + if check_size not in ('extend', 'warn', 'error'): + s.error("Expected one of extend, warn or error, found %r" % check_size) if s.sy != ',': break s.next() - s.expect(']', "Expected 'object' or 'type'") + s.expect(']', "Expected 'object', 'type' or 'check_size'") return objstruct_name, typeobj_name, check_size diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 54259ea50..f924e01b2 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1345,7 +1345,7 @@ class PyExtensionType(PyObjectType): # vtable_cname string Name of C method table definition # early_init boolean Whether to initialize early (as opposed to during module execution). # defered_declarations [thunk] Used to declare class hierarchies in order - # check_size 'min' or boolean What to do if tp_basicsize does not match + # check_size 'warn', 'error', 'extend' What to do if tp_basicsize does not match is_extension_type = 1 has_attributes = 1 @@ -1353,7 +1353,7 @@ class PyExtensionType(PyObjectType): objtypedef_cname = None - def __init__(self, name, typedef_flag, base_type, is_external=0, check_size='min'): + def __init__(self, name, typedef_flag, base_type, is_external=0, check_size=None): self.name = name self.scope = None self.typedef_flag = typedef_flag @@ -1369,7 +1369,7 @@ class PyExtensionType(PyObjectType): self.vtabptr_cname = None self.vtable_cname = None self.is_external = is_external - self.check_size = check_size + self.check_size = check_size or 'warn' self.defered_declarations = [] def set_scope(self, scope): diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 743cd0ebf..e9606b58d 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1482,7 +1482,7 @@ class ModuleScope(Scope): def declare_c_class(self, name, pos, defining=0, implementing=0, module_name=None, base_type=None, objstruct_cname=None, typeobj_cname=None, typeptr_cname=None, visibility='private', - typedef_flag=0, api=0, check_size='min', + typedef_flag=0, api=0, check_size=None, buffer_defaults=None, shadow=0): # If this is a non-extern typedef class, expose the typedef, but use # the non-typedef struct internally to avoid needing forward diff --git a/Cython/Includes/numpy/__init__.pxd b/Cython/Includes/numpy/__init__.pxd index 11eee4e6e..4e5b15d4d 100644 --- a/Cython/Includes/numpy/__init__.pxd +++ b/Cython/Includes/numpy/__init__.pxd @@ -203,7 +203,7 @@ cdef extern from "numpy/arrayobject.h": ctypedef struct PyArray_Descr: pass - ctypedef class numpy.dtype [object PyArray_Descr, check_size False]: + ctypedef class numpy.dtype [object PyArray_Descr, check_size extend]: # Use PyDataType_* macros when possible, however there are no macros # for accessing some of the fields, so some are defined. cdef PyTypeObject* typeobj @@ -239,7 +239,7 @@ cdef extern from "numpy/arrayobject.h": # like PyArrayObject**. pass - ctypedef class numpy.ndarray [object PyArrayObject, check_size False]: + ctypedef class numpy.ndarray [object PyArrayObject, check_size extend]: cdef __cythonbufferdefaults__ = {"mode": "strided"} cdef: diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 77ff3b850..9b2b4acfc 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -319,11 +319,11 @@ static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name, size_t size, int check_size) { /* - * check_size tells what to do if tp_basicsize is different from size: - * 0 - Error (originates in check_size=True) - * 1 - Error if tp_basicsize is smaller, warn if larger (originates in check_size='min') - * 2 - Error if tp_basicsize is smaller (originates in check_size=False) - */ + * 'check_size' tells what to do if tp_basicsize is different from size: + * 0 - Error (originates in check_size=error) + * 1 - Error if tp_basicsize is smaller, warn if larger (originates in check_size=warn) + * 2 - Error if tp_basicsize is smaller, but allow compatible extensions (originates in check_size=extend) + */ PyObject *result = 0; char warning[200]; Py_ssize_t basicsize; diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index 62f9a502b..b9cf890fb 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -771,14 +771,15 @@ Where: - ``object_struct_name`` is the name to assume for the type's C struct. - ``type_object_name`` is the name to assume for the type's statically declared type object. -- ``cs_option`` is ``min`` (the default), ``True``, or ``False`` and is only - used for external extension types. If ``True``, ``sizeof(object_struct)`` must - match the type's ``tp_basicsize``. If ``False``, or ``min``, the - ``object_struct`` may be smaller than the type's ``tp_basicsize``, which - indicates the type allocated at runtime may be part of an updated module, and - that the external module's developers extended the object in a - backward-compatible fashion (only adding new fields to the end of the - object). If ``min``, a warning will be emitted. +- ``cs_option`` is ``warn`` (the default), ``error``, or ``extend`` and is only + used for external extension types. If ``error``, the ``sizeof(object_struct)`` + that was found at compile time must match the type's runtime ``tp_basicsize`` + exactly, otherwise the module import will fail with an error. If ``warn`` + or ``extend``, the ``object_struct`` is allowed to be smaller than the type's + ``tp_basicsize``, which indicates the runtime type may be part of an updated + module, and that the external module's developers extended the object in a + backward-compatible fashion (only adding new fields to the end of the object). + If ``warn``, a warning will be emitted in this case. The clauses can be written in any order. diff --git a/tests/run/check_size.srctree b/tests/run/check_size.srctree index 8391534e4..4d82dd15d 100644 --- a/tests/run/check_size.srctree +++ b/tests/run/check_size.srctree @@ -13,7 +13,7 @@ setup(ext_modules= cythonize("check_size.pyx")) setup(ext_modules = cythonize("_check_size*.pyx")) try: - setup(ext_modules= cythonize("check_size6.pyx")) + setup(ext_modules= cythonize("check_size_invalid.pyx")) assert False except CompileError as e: pass @@ -86,7 +86,7 @@ cdef class Foo: self.field1 = f1 self.field2 = f2 -######## _check_size0.pyx ######## +######## _check_size_exact.pyx ######## cdef extern from "check_size_nominal.h": @@ -99,7 +99,7 @@ cdef extern from "check_size_nominal.h": cpdef public int testme(Foo f) except -1: return f.f0 + f.f1 -######## _check_size1.pyx ######## +######## _check_size_too_small.pyx ######## cdef extern from "check_size_bigger.h": @@ -114,7 +114,7 @@ cpdef public int testme(Foo f, int f2) except -1: f.f2 = f2 return f.f0 + f.f1 + f.f2 -######## _check_size2.pyx ######## +######## _check_size_default.pyx ######## cdef extern from "check_size_smaller.h": @@ -126,12 +126,12 @@ cdef extern from "check_size_smaller.h": cpdef public int testme(Foo f) except -1: return f.f9 -######## _check_size3.pyx ######## +######## _check_size_warn.pyx ######## cdef extern from "check_size_smaller.h": - # make sure missing check_size is equivalent to min - ctypedef class check_size.Foo [object FooStructSmall, check_size min]: + # make sure missing check_size is equivalent to warn + ctypedef class check_size.Foo [object FooStructSmall, check_size warn]: cdef: int f9 @@ -139,12 +139,12 @@ cdef extern from "check_size_smaller.h": cpdef public int testme(Foo f) except -1: return f.f9 -######## _check_size4.pyx ######## +######## _check_size_extend.pyx ######## cdef extern from "check_size_smaller.h": - # Disable size check - ctypedef class check_size.Foo [object FooStructSmall, check_size False]: + # Allow size to be larger + ctypedef class check_size.Foo [object FooStructSmall, check_size extend]: cdef: int f9 @@ -152,12 +152,12 @@ cdef extern from "check_size_smaller.h": cpdef public int testme(Foo f) except -1: return f.f9 -######## _check_size5.pyx ######## +######## _check_size_error.pyx ######## cdef extern from "check_size_smaller.h": # Strict checking, will raise an error - ctypedef class check_size.Foo [object FooStructSmall, check_size True]: + ctypedef class check_size.Foo [object FooStructSmall, check_size error]: cdef: int f9 @@ -165,12 +165,12 @@ cdef extern from "check_size_smaller.h": cpdef public int testme(Foo f) except -1: return f.f9 -######## check_size6.pyx ######## +######## check_size_invalid.pyx ######## cdef extern from "check_size_smaller.h": - # Raise Compilerror when using bad value - ctypedef class check_size.Foo [object FooStructSmall, check_size max]: + # Raise CompileError when using bad value + ctypedef class check_size.Foo [object FooStructSmall, check_size hihi]: cdef: int f9 @@ -180,20 +180,20 @@ cpdef public int testme(Foo f) except -1: ######## runner.py ######## -import check_size, _check_size0, warnings +import check_size, _check_size_exact, warnings foo = check_size.Foo(23, 123, 1023) assert foo.field0 == 23 assert foo.field1 == 123 -ret = _check_size0.testme(foo) +ret = _check_size_exact.testme(foo) assert ret == 23 + 123 # ValueError since check_size.Foo's tp_basicsize is smaller than what is needed # for FooStructBig. Messing with f2 will access memory outside the struct! try: - import _check_size1 + import _check_size_too_small assert False except ValueError as e: assert str(e).startswith('check_size.Foo size changed') @@ -207,25 +207,28 @@ except ValueError as e: with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - import _check_size2 - import _check_size3 + import _check_size_default + import _check_size_warn assert len(w) == 2, 'expected two warnings, got %d' % len(w) assert str(w[0].message).startswith('check_size.Foo size changed') assert str(w[1].message).startswith('check_size.Foo size changed') -ret = _check_size2.testme(foo) +ret = _check_size_default.testme(foo) assert ret == 23 -ret = _check_size3.testme(foo) +ret = _check_size_warn.testme(foo) assert ret == 23 with warnings.catch_warnings(record=True) as w: # No warning, runtime vendor must provide backward compatibility - import _check_size4 + import _check_size_extend assert len(w) == 0 +ret = _check_size_extend.testme(foo) +assert ret == 23 + try: # Enforce strict checking - import _check_size5 + import _check_size_error assert False except ValueError as e: assert str(e).startswith('check_size.Foo size changed') |