Commit c9eda12b authored by Stefan Behnel's avatar Stefan Behnel

Rename the options of the "check_size" feature to make them more obvious:...

Rename the options of the "check_size" feature to make them more obvious: "warn" warns, "error" fails, and "extend" silently allows extending.
Closes #2627.
parent f75b8e8f
......@@ -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" % (
......
......@@ -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
......
......@@ -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
......
......@@ -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):
......
......@@ -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
......
......@@ -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:
......
......@@ -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;
......
......@@ -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.
......
......@@ -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')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment