Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: add check_size option to ctypedef class for external classes #2627

Merged
merged 5 commits into from Sep 29, 2018

Conversation

mattip
Copy link
Contributor

@mattip mattip commented Sep 24, 2018

Address #2221 by adding a new check_size clause to ctypedef class definitions. This will allow suppressing the warning.

The srctree test actually comprises a few tests, but they are reported as one pass/fail. I am not sure how to break them into separate tests.

I think 'min', True, and False were the proposed values, but maybe I misunderstood.

@mattip
Copy link
Contributor Author

mattip commented Sep 24, 2018

Of course, we can fix the original issue by using this in numpy.__init__.pxd.

@mattip
Copy link
Contributor Author

mattip commented Sep 24, 2018

Test failures from cpp, did I cause that?

compiling (cpp) and running extmember ... 
=== C/C++ compiler output: =========
pointers.cpp: In function ‘PyObject* __pyx_pf_8pointers_compare(PyObject*)’:
pointers.cpp:1135:65: warning: self-comparison always evaluates to true [-Wtautological-compare]

@mattip
Copy link
Contributor Author

mattip commented Sep 25, 2018

Yes, it seems filename.pxd creates a filename_api.h without the proto section, so it misses the declaration of the enum I tried to create.

Copy link
Contributor

@scoder scoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nicely written PR, sorry for the load of comments. I think we should still get this into 0.29.
The travis failure is just some whitespace left-over, found by the style checker.

* 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this doesn't look like what I'd expect a check_size=False to do … I mean, it's probably the right behaviour, but then False is not the right setting. (Open for debate, I guess …)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we always need to error if tp_basicsize is smaller than the C struct declared as the object, see the _check_size1.pyx test where I define a C struct that is bigger. Accessing f2 would touch invalid memory.

So I claim even if using False we need to error on the side of caution.Open to other suggestions for the name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the options correctly, we always error fo smaller structs, but either ignore, warn, or error for larger structs. This doesn't map obviously to the True/False/'min' options provided here--maybe literally call then None, 'warn,' and 'error'? (As an aside, maybe it'd be better to use an enum than 0, 1, 2 as well).

(I actually wonder if instead we'd be better off doing a stricter check using offsetof for declared fields, even for non-extern classes, but that may have issues...)

Copy link
Contributor

@scoder scoder Sep 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about "extend", "warn" (default) and "error" ? None doesn't tell me much either.

Agreed that offset checks would be nice, but that's a separate feature (and not for 0.29).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it'd be better to use an enum than 0, 1, 2

I tried that, but an enum just before the __Pyx_ImportType declaration in the TypeImport.proto section was not exported to the generated code. Is there an example of such an enum?

@@ -736,6 +736,14 @@ built-in complex object.::
...
} PyComplexObject;

At runtime, a check will be performed when importing the cython
c-extension module that ``__builtin__.complex``'s ``tp_basicsize``
matches ``sizeof(`PyComplexObject)``. This check can fail if in the cython
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No "in", just "if the Cython …".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -736,6 +736,14 @@ built-in complex object.::
...
} PyComplexObject;

At runtime, a check will be performed when importing the cython
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please spell the name "Cython" with a capital "C". (You are not referring to the command line utility here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

c-extension module that ``__builtin__.complex``'s ``tp_basicsize``
matches ``sizeof(`PyComplexObject)``. This check can fail if in the cython
c-extension module was compiled with one version of the
``complexobject.h`` header but imported into a python with a changed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing, I think you are referring to a "Python" runtime here, not the "python" executable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

in_pxd = ctx.level == 'module_pxd',
doc = doc,
body = body)

def p_c_class_options(s):
objstruct_name = None
typeobj_name = None
check_size = b'min'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have this a normal text string rather than a bytes string. Comparison to bytes might fail then parsed from Py3 code with unicode literals.
One way to do that might be to use the identifier min instead of the string literal "min". Or check the literal node type and decode at need.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is messy since we want min, True, or False. Changing from p_atom(s) to p_ident(s) makes True and False into strings not booleans, is that OK? In any case, that enables removing the b (as long as we use a bare min not 'min'. Changed documentation (which was missing quotes anyway) and test too.

- ``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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"only used for external extension types" - does the patch check that it's not used in other contexts? (And, are there actually other contexts?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was relating to the if not type.is_external or type.is_subclassed check done in ModuleNode.py, which I admit I don't really understand. See last comment below. A suggestion for rephrasing that better catches the logic is welcome

runtests.py Outdated
command, self._try_decode(out), self._try_decode(err)))
for c, o, e in zip(cmd, out, err):
sys.stderr.write("%s\n%s\n%s\n\n" % (
c, self._try_decode(o), self._try_decode(e)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already part of the other PR, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry. Removing.

type.typeptr_cname,
error_code))
# check_size
if not type.is_external or type.is_subclassed:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be better to raise a compiler error here if check_size was set. Or at least warn that the value was ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what these values mean. It is from the original code and the source of the documentation problem you pointed out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check added, test needed. Suggestion for ctypedef stanza that will trigger the failure welcome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"is_external" = defined in a "cdef extern" block. False if a type is cimported from another module via a .pxd file.
"is_subclassed" = we found a subtype of the type, i.e. it is definitely not a leaf class of the hierarchy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or do we want to allow this for non-extern classes? (We need to take care that the vtable doesn't change either...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the type is cimported, how would we set check_size? Is there an example in the tests that redefines a cimported type?

@mattip
Copy link
Contributor Author

mattip commented Sep 25, 2018

Maybe worth adding some additional tests for failures and corner cases? Can we somehow write them as non-srctree tests?

@scoder
Copy link
Contributor

scoder commented Sep 25, 2018

Can we somehow write them as non-srctree tests?

Don't think so. You need at least two files in order to cimport from the other.
You migth be able to cover multiple cases with the same srctree file, though. These tests are quite expensive to run, so it's not uncommon to cover an entire (little) feature with one test setup.

type.typeptr_cname,
error_code))
# check_size
if not type.is_external or type.is_subclassed:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"is_external" = defined in a "cdef extern" block. False if a type is cimported from another module via a .pxd file.
"is_subclassed" = we found a subtype of the type, i.e. it is definitely not a leaf class of the hierarchy.

Cython/Compiler/Parsing.py Show resolved Hide resolved
@mattip
Copy link
Contributor Author

mattip commented Sep 29, 2018

Thanks for merging. Now on to using this in the numpy pxd ...

@scoder
Copy link
Contributor

scoder commented Sep 29, 2018

Could you quickly write up a PR for that? I'd like to get the second RC out today.

@scoder
Copy link
Contributor

scoder commented Sep 29, 2018

Actually, I'll just enable check_size=False for the ndarray and dtype types, that should be it.

@mattip
Copy link
Contributor Author

mattip commented Sep 29, 2018

Thanks @scoder

scoder added a commit that referenced this pull request Sep 29, 2018
…ous: "warn" warns, "error" fails, and "extend" silently allows extending.

Closes #2627.
@scoder
Copy link
Contributor

scoder commented Sep 29, 2018

I changed the available options to "extend", "warn" and "error". The boolean values really were everything but obvious.

@robertwb
Copy link
Contributor

robertwb commented Sep 29, 2018 via email

@scoder
Copy link
Contributor

scoder commented Sep 30, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants