-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Rationalize the different cline traceback options #6036
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
Conversation
It's still unused even if we don't set it, so take it out of the bit that sets it conditionally.
* If the user has specified the c_line_in_traceback=False option to the Cython compiler, then set CYTHON_CLINE_IN_TRACEBACK to avoid generating all the code to print clines out. * If the user has defined the C flag CYTHON_CLINE_IN_TRACEBACK then skip recording the C line on an exception (since we know this is expensive in terms of code size, and we'll never actually use what we've recorded)
|
Here's a proposal that reduces the whole thing to checking code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK")
code.putln(f"#define CYTHON_CLINE_IN_TRACEBACK {'1' if options.c_line_in_traceback else '0'}")
code.putln("#endif")
# Using "(void)cname" to prevent "unused" warnings.
mark_errpos_code = (
"#define __PYX_MARK_ERR_POS(f_index, lineno) {"
f" {Naming.filename_cname} = {Naming.filetable_cname}[f_index];"
f" (void) {Naming.filename_cname};"
f" {Naming.lineno_cname} = lineno;"
f" (void) {Naming.lineno_cname};"
"%s" # for C line info
"}"
)
cline_info = (
f" {Naming.clineno_cname} = {Naming.line_c_macro};"
f" (void) {Naming.clineno_cname}; "
)
code.putln("#if CYTHON_CLINE_IN_TRACEBACK")
code.putln(mark_errpos_code % cline_info)
code.putln("#else")
code.putln(mark_errpos_code % "")
code.putln("#endif") |
This changes the current behaviour. Currently:
I'm not against changing the current behaviour, but we should be aware we're doing it. |
|
Argh, right. I forgot about the runtime configuration part. It's incredible in retrospect how much work we put into this feature. It certainly is a helpful debugging feature, but it should rather remain at that and not get in the way too much. Second try: # Error handling and position macros.
# Using "(void)cname" to prevent "unused" warnings.
mark_errpos_code = (
"#define __PYX_MARK_ERR_POS(f_index, lineno) {"
f" {Naming.filename_cname} = {Naming.filetable_cname}[f_index];"
f" (void) {Naming.filename_cname};"
f" {Naming.lineno_cname} = lineno;"
f" (void) {Naming.lineno_cname};"
"%s" # for C line info
"}"
)
cline_info = (
f" {Naming.clineno_cname} = {Naming.line_c_macro};"
f" (void) {Naming.clineno_cname}; "
)
if not options.c_line_in_traceback:
code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK")
code.putln("#define CYTHON_CLINE_IN_TRACEBACK 0")
code.putln("#endif")
code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK) || CYTHON_CLINE_IN_TRACEBACK")
code.putln(mark_errpos_code % cline_info)
code.putln("#else")
code.putln(mark_errpos_code % "")
code.putln("#endif")
code.putln("#define __PYX_ERR(f_index, lineno, Ln_error) \\")
code.putln(" { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; }") |
|
That looks good with one small change: I'll try that |
|
EDIT: Well, again, the above doesn't work with the runtime config. But the following still holds: In fact, I'd rather deprecate the Cython option(s) in 3.1 and let users decide about it entirely at C compile time. It's easy to set C macros, maybe even easier than setting a Cython option, for many users. |
|
Right. That gives us this then: # Error handling and position macros.
# Using "(void)cname" to prevent "unused" warnings.
mark_errpos_code = (
"#define __PYX_MARK_ERR_POS(f_index, lineno) {"
f" {Naming.filename_cname} = {Naming.filetable_cname}[f_index];"
f" (void) {Naming.filename_cname};"
f" {Naming.lineno_cname} = lineno;"
f" (void) {Naming.lineno_cname};"
"%s" # for setting C line info
f" (void) {Naming.clineno_cname}; "
"}"
)
set_cline_info = (
f" {Naming.clineno_cname} = {Naming.line_c_macro};"
)
if not options.c_line_in_traceback:
code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK")
code.putln(f"#define CYTHON_CLINE_IN_TRACEBACK 0")
code.putln("#endif")
code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK) || CYTHON_CLINE_IN_TRACEBACK")
code.putln(mark_errpos_code % set_cline_info)
code.putln("#else")
code.putln(mark_errpos_code % "")
code.putln("#endif") |
|
How about we change to
(Defaulting to either 0 or -1 - I don't really mind). That means it can be controlled completely by the C define, and we can deprecate the options flag as you propose) Edit: The only advantage in doing this is if we want to turn it to 0 by default though |
|
* 0 for disabled
* 1 for enabled
* -1 for runtime
My first thought was -1, too, but I think 2 is better. It's like 1 (i.e. on) but more. :)
I also think that an explicit value is better than letting users figure out how to undefine it.
|
scoder
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I just noticed that using 2 for the macro in new setups is not backwards compatible. Let's stick with undefined vs. defined then.
Co-authored-by: scoder <[email protected]>
This reverts commit 4757126.
|
mention versions
We should still make it clear that the macro also works in older versions than 3.1. Users should generally migrate away from the options, not just for new Cython versions.
|
|
My last comment in this PR and it is just personal opinion. Please keep in mind that recently I have very limited free time so I did not went in deep so maybe I misunderstood something. If I understand correctly default behavior (if you provide no macro and no runtime time option) is to have ~5% overhead present. In my humble opinion is that this is not what users want because:
Having said all above, I think that we should generate binary without overhead and allow the users to add it only if they needs it. Hence the default behaviour should be equal to setting macro |
|
default behaviour should be equal to setting macro CYTHON_CLINE_IN_TRACEBACK=0.
I agree with this. Let's see if we can find a way to make it easy for users to use the same setup in Cython 3.0 (and earlier?) and 3.1.
|
The only way I can think to easily to that if to add a second macro (e.g.
I don't really like it because it's two defines when it should be one. But I can't think of another way to achieve this. |
I agree with @da-woods . It depends what is less evil - current default overhead caused by C lines or 2 macros. I vote for having 2 macros. @scoder @da-woods what is your oppinion? Maybe we can do following:
It is more cumbersome but it enables us to clean it up in future releases... |
scoder
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still needs these changes in Exceptions.c (untested):
/////////////// CLineInTraceback.proto ///////////////
if CYTHON_CLINE_IN_TRACEBACK_RUNTIME
static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);/*proto*/
#else
#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0)
#endif
/////////////// CLineInTraceback ///////////////
//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError
//@requires: ObjectHandling.c::PyDictVersioning
//@requires: PyErrFetchRestore
//@substitute: naming
#if CYTHON_CLINE_IN_TRACEBACK_RUNTIME
static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) {
Cython/Compiler/ModuleNode.py
Outdated
| if not options.c_line_in_traceback: | ||
| code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK") | ||
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK 0") | ||
| code.putln("#endif") | ||
| code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK) || CYTHON_CLINE_IN_TRACEBACK") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope I got all combinations right…
| if not options.c_line_in_traceback: | |
| code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK") | |
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK 0") | |
| code.putln("#endif") | |
| code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK) || CYTHON_CLINE_IN_TRACEBACK") | |
| # 1) "CYTHON_CLINE_IN_TRACEBACK=0" always disables C lines in tracebacks | |
| # 2) "options.c_line_in_traceback=False" changes the default to "no runtime configuration" | |
| # 3) "CYTHON_CLINE_IN_TRACEBACK_RUNTIME=1" enables the feature + runtime configuration | |
| # 4) "CYTHON_CLINE_IN_TRACEBACK=1" enables the feature without runtime configuration | |
| code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK_RUNTIME)") | |
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME 0") | |
| if options.c_line_in_traceback: | |
| code.putln("#elif !defined(CYTHON_CLINE_IN_TRACEBACK)") | |
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK (CYTHON_CLINE_IN_TRACEBACK_RUNTIME)") | |
| code.putln("#elif defined(CYTHON_CLINE_IN_TRACEBACK) && !CYTHON_CLINE_IN_TRACEBACK") | |
| code.putln("#undef CYTHON_CLINE_IN_TRACEBACK_RUNTIME") | |
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME 0") | |
| code.putln("#endif") | |
| code.putln("#if !defined(CYTHON_CLINE_IN_TRACEBACK)") | |
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK 0") | |
| code.putln("#endif") | |
| code.putln("#if CYTHON_CLINE_IN_TRACEBACK") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this a bit difficult to follow, so rewrote it in a way I found more obvious. I think the core behaviour is basically as you describe although some of the edge cases where the different options contradict each other might be different.
I've also added a fairly thorough set of tests I think (so feel free to undo my rewrite since the tests should at least confirm it's doing the right thing now)
|
I've proposed such a 2-macros implementation, making some handwavy assumptions.
Thus, we should optimise for the "feature disabled" case, make it easy to enable the feature, and provide a way to configure it at runtime. I think two separate macros model this quite ok. |
|
Worked it out, but it's not quite what I thought |
| #: Number of function closure instances to keep in a freelist (0: no freelists) | ||
| closure_freelist_size = 8 | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidental removal?
| code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK_RUNTIME") | ||
| code.putln(f"#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME {default_cline_runtime}") | ||
| code.putln("#endif") | ||
|
|
||
| code.putln("#ifndef CYTHON_CLINE_IN_TRACEBACK") | ||
| code.putln("#define CYTHON_CLINE_IN_TRACEBACK CYTHON_CLINE_IN_TRACEBACK_RUNTIME") | ||
| code.putln("#endif") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This misses the case of CYTHON_CLINE_IN_TRACEBACK_RUNTIME=1 and CYTHON_CLINE_IN_TRACEBACK=0. Your implementation will generate the runtime utility code but it won't find C lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think this should now be covered
| .. autodata:: Cython.Compiler.Options.buffer_max_dims | ||
| .. autodata:: Cython.Compiler.Options.closure_freelist_size | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidental removal?
|
|
||
| ============================================ ======================================================================================================================================= | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidental removal?
I consider it a good thing to keep double empty lines between sections, to give them a better visual separation.
Co-authored-by: scoder <[email protected]>
| if hasattr(ext, 'no_c_in_traceback'): | ||
| c_line_in_traceback = not ext.no_c_in_traceback | ||
| else: | ||
| c_line_in_traceback = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking closer our distutils extensions also mess with the value. I've changed this one but not old_build_ext
| emit_linenums=False, | ||
| relative_path_in_code_position_comments=True, | ||
| c_line_in_traceback=True, | ||
| c_line_in_traceback=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we now also have to declare the type of the option as bool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand this comment? Possibly you're referring to directive_types (which applies to directives rather than options?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I meant. Sorry, I mixed up the two.
Then we still need to make sure that we receive a boolean value, regardless of what users pass us. We shouldn't report misuse by a failing int() call, and we shouldn't let users pass arbitrary integer values into the C code.
Co-authored-by: scoder <[email protected]>
scoder
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Looking at the test, I don't see a reason why there should be that many setup.py files. Just one to build them all should be enough, really. And given that the test files are specific to a test case and expected outcome, why not move the macro definitions into the test files with # distutils: define_macros= …? That would move things together that belong together, and make the whole test setup much easier to read and extend.
to the Cython compiler, then set CYTHON_CLINE_IN_TRACEBACK=0 to
avoid generating all the code to print clines out.
skip recording the C line on an exception (since we know this
is expensive in terms of code size, and we'll never actually
use what we've recorded)
PR also includes commits from #6035 so merge that first.
This should probably be 3.1 only.