| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Conflicts: Parser/parsetok.c
There was a problem hiding this comment.
I'm very excited for this change
Sorry, something went wrong.
| The UTF-8 offset is recorded because the parser uses UTF-8 internally. | ||
|
|
||
| Note that the end positions are not required by the compiler and are | ||
| therefore optional. The end offset if *after* the last symbol, for example |
There was a problem hiding this comment.
| therefore optional. The end offset if *after* the last symbol, for example | |
| therefore optional. The end offset is *after* the last symbol, for example |
Sorry, something went wrong.
| This is useful to "move code" to a different location in a file. | ||
| Increment the line number and end line number of each node in the tree | ||
| starting at *node* by *n*. This is useful to "move code" to a different | ||
| location in a file.. |
There was a problem hiding this comment.
| location in a file.. | |
| location in a file. |
Sorry, something went wrong.
There was a problem hiding this comment.
Phew! I don't have much to add, this all looks great. I don't think in these days 20% extra space to represent CST+AST for a single module is a big deal (though it would be nice to see what the difference is in absolute number of bytes for Lib/tkinter/init.py, which AFAIK is still the largest stdlib module).
Sorry, something went wrong.
| to *new_node* if possible, and return *new_node*. | ||
| Copy source location (:attr:`lineno`, :attr:`col_offset`, :attr:`end_lineno`, | ||
| and :attr:`end_col_offset`) from *old_node* to *new_node* if possible, | ||
| and return *new_node*. |
There was a problem hiding this comment.
Does this only affect the node, or the whole tree? I've never used this so I don't know what to expect from context.
Sorry, something went wrong.
There was a problem hiding this comment.
This one (unlike some others) is non-recursive, one can even copy from a node of a different kind.
Sorry, something went wrong.
| @@ -438,152 +446,188 @@ mod_ty _Py_Interactive(asdl_seq * body, PyArena *arena); | |||
| mod_ty _Py_Expression(expr_ty body, PyArena *arena); | |||
There was a problem hiding this comment.
A few lines up, I wonder if it would be a good idea to add a comment explaining that these macros also change the definition of the functions (not just the calls).
Sorry, something went wrong.
There was a problem hiding this comment.
Added a comment.
Sorry, something went wrong.
| _fix(child, lineno, col_offset) | ||
| _fix(node, 1, 0) | ||
| _fix(child, lineno, col_offset, end_lineno, end_col_offset) | ||
| _fix(node, 1, 0, 1, 0) |
There was a problem hiding this comment.
This whole function looks a bit suspicious.Shouldn't it at least ensure that (end_lineno, end_col_offset) > (lineno, col_offset)? (Again, I guess I don't know the use case.)
Sorry, something went wrong.
There was a problem hiding this comment.
This function is used to compile manually generated AST to bytecode. Although technically end positions are not needed for the compiler (only start positions are recorded in the byte code), it is probably good to have them fixed. The algorithm for fixing is quite naive (take either a position of nearest root-side node if known, otherwise use start of the file), but it never intended to be robust, it exist just to allow compilation in those case where a user doesn't care about line numbers.
Sorry, something went wrong.
|
|
||
| if end_lineno == lineno: | ||
| source_line = source.splitlines()[lineno] | ||
| return source_line.encode()[col_offset:end_col_offset].decode(coding) |
There was a problem hiding this comment.
So if I read this well, source is bytes, and the col_offsets are byte offsets? And a tab counts as one space? Is that explicit somewhere in the ast module docs? (This is easy to miss when writing code since most source code is ASCII only so the difference doesn't matter, but it would break whenever a token appears after a non-ASCII character in an identifier or string literal.)
Sorry, something went wrong.
There was a problem hiding this comment.
All you say is right, and yes it is already mentioned in the docs that they are UTF-8 byte offsets.
Sorry, something went wrong.
| last = lines[end_lineno].encode()[:end_col_offset].decode(coding) | ||
| middle_lines = lines[lineno+1:end_lineno] | ||
|
|
||
| return '\n'.join([first] + middle_lines + [last]) |
There was a problem hiding this comment.
This would avoid two list copies if you used .insert() and .append() to merge first and last into the middle_lines list.
Sorry, something went wrong.
There was a problem hiding this comment.
Fixed.
Sorry, something went wrong.
| self.assertEqual(ast.get_source_segment(s_orig, cdef.body[0], padded=True), | ||
| s_method) | ||
|
|
||
|
|
There was a problem hiding this comment.
FWIW I trust your tests. If they pass, they're good. :-)
Sorry, something went wrong.
| @@ -0,0 +1 @@ | |||
| Add end line and end column position information to the Python AST nodes. No newline at end of file | |||
There was a problem hiding this comment.
Maybe add that this is a backwards incompatible API change for anything that creates AST nodes? (It's fine if you just call ast.parse(), but not if you ever create a new node.)
Sorry, something went wrong.
There was a problem hiding this comment.
Actually most of the things still work:
However, some C-level compatibility is broken. I am not sure what is the source of truth for "official" C API. I checked docs.python.org and none of the functions I updated is listed there.
I will try to re-formulate this.
Sorry, something went wrong.
| } | ||
|
|
||
| return Call(func, args, keywords, func->lineno, func->col_offset, c->c_arena); | ||
| // Ideally call ends where closing parenthesis is placed. |
There was a problem hiding this comment.
(When does a call not have a close par?)
Sorry, something went wrong.
There was a problem hiding this comment.
It (close par) was NULL for "fake" calls used in analyzing class definitions, but I just fixed this and removed the comment.
Sorry, something went wrong.
| get_last_end_pos(asdl_seq *s, int *end_lineno, int *end_col_offset) | ||
| { | ||
| int tot = asdl_seq_LEN(s); | ||
| stmt_ty last = asdl_seq_GET(s, tot - 1); |
There was a problem hiding this comment.
Does this need a check that tot > 0?
Sorry, something went wrong.
There was a problem hiding this comment.
Added this just in case (to be future proof against any AST optimizers).
Sorry, something went wrong.
There was a problem hiding this comment.
This is a part of comments.
Sorry, something went wrong.
| :class:`AsyncFunctionDef` is now supported. | ||
|
|
||
|
|
||
| .. function:: get_code_segment(source, node, *, padded=False, coding='utf-8') |
There was a problem hiding this comment.
I think encoding is more common term in this context.
Sorry, something went wrong.
| The UTF-8 offset is recorded because the parser uses UTF-8 internally. | ||
|
|
||
| Note that the end positions are not required by the compiler and are | ||
| therefore optional. The end offset if *after* the last symbol, for example |
There was a problem hiding this comment.
What does "optional" mean? Is it equal to None or an absent attribute?
Sorry, something went wrong.
There was a problem hiding this comment.
Actually both are allowed. An optional attribute in Python.asdl can either be absent or None.
Sorry, something went wrong.
| If *padded* is `True`, the first line of a multi-line statement will | ||
| be padded with spaces to match its original position. | ||
| """ | ||
| if not hasattr(node, 'lineno'): |
There was a problem hiding this comment.
This can be simplified:
try:
# get loneno, end_lineno, etc.
except AttributeError:
return None
Sorry, something went wrong.
There was a problem hiding this comment.
Done.
Sorry, something went wrong.
|
|
||
| if end_lineno == lineno: | ||
| source_line = source.splitlines()[lineno] | ||
| return source_line.encode()[col_offset:end_col_offset].decode(coding) |
There was a problem hiding this comment.
I think coding should not be used here. Always use UTF-8.
Sorry, something went wrong.
There was a problem hiding this comment.
You are totally right.
Sorry, something went wrong.
| lines = source.splitlines() | ||
|
|
||
| if padded: | ||
| padding = ' ' * len(lines[lineno].encode()[:col_offset].decode(coding)) |
There was a problem hiding this comment.
I suggest to keep tabs and spaces (and maybe '\f'?) and replace all other characters with spaces. There may be similar code in C, look how the position of the caret is calculated.
Sorry, something went wrong.
There was a problem hiding this comment.
Oh yes. It looks like there are bunch of corner cases around \r\n\f\t. I tried to update the function (including your suggestions) so that it mimics closely what happens in C code.
Sorry, something went wrong.
| self.assertEqual(len(stmt.attributes), 2) | ||
| self.assertEqual(len(stmt.attributes), 4) | ||
| self.assertEqual(str(stmt.attributes[0]), 'Field(int, lineno)') | ||
| self.assertEqual(str(stmt.attributes[1]), 'Field(int, col_offset)') |
There was a problem hiding this comment.
Add tests for stmt.attributes[2] and stmt.attributes[3]?
Sorry, something went wrong.
There was a problem hiding this comment.
Added.
Sorry, something went wrong.
|
|
||
| int | ||
| PyNode_AddChild(node *n1, int type, char *str, int lineno, int col_offset) | ||
| PyNode_AddChild(node *n1, int type, char *str, int lineno, int col_offset, |
There was a problem hiding this comment.
Is not this a part of the C API? Should not we add a new function?
Sorry, something went wrong.
There was a problem hiding this comment.
What is the "official" source of truth for C API? I didn't find this function on docs.python.org.
Sorry, something went wrong.
| @@ -225,7 +229,9 @@ future_hack(parser_state *ps) | |||
|
|
|||
| int | |||
| PyParser_AddToken(parser_state *ps, int type, char *str, | |||
There was a problem hiding this comment.
Is not this a part of the C API?
Sorry, something went wrong.
There was a problem hiding this comment.
Same here.
Sorry, something went wrong.
|
@asottile @gvanrossum @serhiy-storchaka Thank you for reviewing! I think I addressed all your comments. |
Sorry, something went wrong.
| return text | ||
|
|
||
|
|
||
| def _splitlines_no_ff(source): |
There was a problem hiding this comment.
Another option is to use .splitlines(keepends=True) (which considers \f as an endline) and then re-join those that aren't ending in \r or \n, I am not sure which approach is better.
Sorry, something went wrong.
|
@gvanrossum I just tried to compare the sizes of Lib/tkinter/__init__.py AST before and after this PR. Here are the numbers: before 5.8 Mbytes, after 7.2Mbytes (24% increase). |
Sorry, something went wrong.
How did you measure this? IIUC there's also a size increase for the CST, and during translation from CST to AST both are in memory. Is it possible to measure the peak memory usage during this translation? |
Sorry, something went wrong.
|
I just looked at the difference in allocated memory reported in sys._debugmallocstats(). I just tried another way using tracemalloc. It reports that the line in question: compile(source, filename, mode, PyCF_ONLY_AST)allocates 3.7 Mbytes before and 4.6 Mbytes after this PR (still the same 24% relative increase). Also this number (24%) is quite reasonable taking into account that all nodes in CST have exactly the same size: 40 bytes before, 48 bytes after (at least on my 64-bit Linux). |
Sorry, something went wrong.
|
I do think that's a very reasonable increase (we're not talking MicroPython here :-).
That's sizeof what exactly? While the node types indeed are big unions, there are several different ones: struct _stmt, struct _expr and some minor ones (all defined in Include/Python-ast.h). (I tried this myself on my Mac, and it looks like sizeof(struct _stmt) is 72 bytes, and sizeof(struct _expr) is 48 bytes. An int is 4 bytes.) |
Sorry, something went wrong.
This is a size of _node. There is only one "main" struct in CST struct _node from node.h. |
Sorry, something went wrong.
There was a problem hiding this comment.
I like this! Let's land this. I combed through the code (both the tedious parts and the interesting parts) and it all looks good to me. I am not worried about the 25% increase in CST and AST tree sizes.
Sorry, something went wrong.
|
@gvanrossum |
Sorry, something went wrong.
I'm not sure. Their names don't have _ prefixes so I think they live in some nebulous area. It would be good to bring this up in another forum where more core devs can think about the issue (some of whome have probably thought about it more). In the worst case scenario, if it's deemed an unacceptable backwards incompatibility, we can always add new functions that add end_lineno and end_col_offset arguments, and make the old ones call the new ones with some kind of default values computed. But I would only resort to this if during the alpha/beta release we get actual complaints. |
Sorry, something went wrong.
|
OK, I will post to Python-Dev about this. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
The majority of this PR is tediously passing end_lineno and end_col_offset everywhere. Here are non-trivial points:
An (inevitable) downside of this PR is that AST now takes almost 25% more memory. I think however it is probably justified by the benefits.
https://bugs.python.org/issue33416