GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — compile() with PyCF_ONLY_AST flag ignores the optimize arg · Issue #108113 · python/cpython

compile() with PyCF_ONLY_AST flag ignores the optimize arg #108113

New issue
Copy link
New issue
Copy link
Closed
#108154
Closed
compile() with PyCF_ONLY_AST flag ignores the optimize arg#108113
#108154
Copy link
Assignees
iritkatriel
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@iritkatriel
iritkatriel
opened on Aug 18, 2023
Issue body actions

compile() (or ast.parse(), which calls it) can return the ast for some source code, converted to a Python object version of the AST. But this AST is not optimized (with _PyAST_Optimize).

Static Python re-implemented _PyAST_Optimize for this reason. If we expose a way for them to get an optimized AST, they won't have to.

For performance, it would be better not to call another API function on a Python AST, but to add an API option that runs _PyAST_Optimize before converting the AST to Python.

Linked PRs

  • gh-108113: Make it possible to create an optimized AST #108154
  • gh-108113: Make it possible to optimize an AST #108282
  • gh-108113: docs : mention PyCF_OPTIMIZED_AST in ast Compiler Flags #113241
Reacted by Carl Meyer

Activity

Next
  1. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    I think we can use the optimize parameter for this. When the ast.PyCF_ONLY_AST flag is set, the optimize flag is not currently used at all (this is arguably a bug because the AST should replace __debug__ by True or False).

    We can decide that when ast.PyCF_ONLY_AST and optimize >= 2 then we return an optimized AST.

  2. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    Currently:

    >>> ast.dump(compile("x = __debug__", "f.py", "exec", flags = ast.PyCF_ONLY_AST, optimize=2))
    "Module(body=[Assign(targets=[Name(id='x', ctx=Store())], value=Name(id='__debug__', ctx=Load()))], type_ignores=[])"
    >>> 
    

    With the suggested change:

    >>> ast.dump(compile("x = __debug__", "f.py", "exec", flags = ast.PyCF_ONLY_AST, optimize=2))
    "Module(body=[Assign(targets=[Name(id='x', ctx=Store())], value=Constant(value=False))], type_ignores=[])"
    
    Reacted by sunmy2019
  3. carljm commented on Aug 18, 2023

    @carljm
    carljm
    on Aug 18, 2023
    Member
    More actions

    Currently the design is that the optimize level is taken by _PyAST_Optimize as an argument, and then used internally by the AST optimizer to decide what and how to optimize.

    So rather than deciding to call _PyAST_Optimize only if optimize >= 2, would it be more consistent to just say that _PyAST_Optimize should be called anytime we build an AST, and continue to let it decide internally what to do based on the optimize level?

    Presumably for optimize = 0 at least, that should result in no change in behavior.

  4. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    Another thing to decide is whether we want this to work when the input is already an AST. Currently if ast.PyCF_ONLY_AST then the AST is just returned (a new reference to it). I think we should make the new API optimize the AST (convert it to a C AST, optimize, and then convert back to Python).

  5. carljm commented on Aug 18, 2023

    @carljm
    carljm
    on Aug 18, 2023
    Member
    More actions

    Hmm, my leaning would be rather to stick with the current behavior (return input unchanged) if the input is already an AST and you request an AST. IMO it's too subtle for compile to implicitly try to re-optimize a given AST. If we want a way to take a constructed AST and run the optimizer over it, I would provide that as dedicated API: ast.optimize(...)?

    Reacted by Irit Katriel
  6. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    So rather than deciding to call _PyAST_Optimize only if optimize >= 2, would it be more consistent to just say that _PyAST_Optimize should be called anytime we build an AST, and continue to let it decide internally what to do based on the optimize level?

    Presumably for optimize = 0 at least, that should result in no change in behavior.

    My concern is that in normal compilation we would end up calling _PyAST_Optimize twice, because it's called in compile.c when we start the compilation. We could try to pull it out and make _PyAST_Compile expect an optimized AST, but I'm not sure what that might break.

  7. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    Note that _PyAST_Compile takes its own version of the flags.

  8. carljm commented on Aug 18, 2023

    @carljm
    carljm
    on Aug 18, 2023
    Member
    More actions

    My concern is that in normal compilation we would end up calling _PyAST_Optimize twice, because it's called in compile.c when we start the compilation. We could try to pull it out and make _PyAST_Compile expect an optimized AST, but I'm not sure what that might break.

    Isn't this a problem regardless, for optimize = 2?

    I think the main back-compat issue would be if there are C extensions currently calling _PyAST_Compile (with an AST they have in some way constructed themselves) and expecting AST optimization to occur. I agree this is possible. One way to address it could be to add a new compiler flag that says "AST already optimized" and skip _PyAST_Optimize in _PyAST_Compile iff this flag is set?

    Or another (IMO less clean, but perhaps also less invasive) option would be to call _PyAST_Optimize in builtin_compile iff PyCF_ONLY_AST is set. Then we'd still optimize only once in the case of a full compile?

  9. iritkatriel commented on Aug 18, 2023

    @iritkatriel
    iritkatriel
    on Aug 18, 2023
    MemberAuthor
    More actions

    Or another (IMO less clean, but perhaps also less invasive) option would be to call _PyAST_Optimize in builtin_compile iff PyCF_ONLY_AST is set. Then we'd still optimize only once in the case of a full compile?

    This is what I was thinking we'd do.

  10. carljm commented on Aug 18, 2023

    @carljm
    carljm
    on Aug 18, 2023
    Member
    More actions

    I think that's reasonable, but in that case I see no reason to also limit it to optimize >= 2 -- I think we can safely just always call it and let _PyAST_Optimize make use of the optimize level?

    Reacted by Irit Katriel
  11. iritkatriel
    changed the title [-]Add a way to get an optimized AST[/-] [+]compile() with PyCF_ONLY_AST flag ignores the optimize arg[/+] on Aug 18, 2023
  12. iritkatriel
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Aug 18, 2023
  13. iritkatriel
    added a commit that references this issue on Aug 18, 2023

    pythongh-108113: optimize ASTs in ast.parse/ast.literal_eval/compile(…

    c491162
  14. iritkatriel commented on Aug 19, 2023

    @iritkatriel
    iritkatriel
    on Aug 19, 2023
    MemberAuthor
    More actions

    As discussed offline, the AST optimization performs const folding regardless of the value of optimize, so calling it always it too disabling. We'll go with a new flag ast.PyCF_OPTIMIZED_AST.

  15. corona10 commented on Aug 20, 2023

    @corona10
    corona10
    on Aug 20, 2023
    Member
    More actions

    Maybe this is another topic with this issue,
    but I feel the need for a flag that can be used for not applying the CFG optimization phase for debugging purposes.
    (To observe the origin bytecodes and how they changed)

    Do you think that it's worth adding it too?
    If so, I would like to introduce a new mode of compile flag PyCF_DISABLE_CFG_OPT(I am not sure where the flag should exist, AST looks not proper, but no proper places exist), and then if the PyCF_DISABLE_CFG_OPT flag is passed from builtin.compile then optimize_and_assemble_code_unit will not apply the related optimization process.

    WDYT?

    There might be another way to deal with this issue too.

  16. 1 remaining item

  17. corona10 commented on Aug 20, 2023

    @corona10
    corona10
    on Aug 20, 2023
    Member
    More actions

    Ah for debugging purposes, it will be enough.

  18. iritkatriel
    closed this as completedin #108154on Aug 21, 2023
  19. iritkatriel
    added a commit that references this issue on Aug 21, 2023

    gh-108113: Make it possible to create an optimized AST (#108154)

    10a91d7
  20. carljm commented on Aug 21, 2023

    @carljm
    carljm
    on Aug 21, 2023
    Member
    More actions

    Ironically the solution we chose does not actually fix the issue as described in the issue title :) But I think it's OK, we now clearly present an alternative option that does respect the optimize flag.

    Reacted by Irit Katriel
  21. iritkatriel commented on Aug 21, 2023

    @iritkatriel
    iritkatriel
    on Aug 21, 2023
    MemberAuthor
    More actions

    I think we should make ast.parse() optimize also when the input is an AST. At the moment there is no way to apply AST optimization to an AST that you created, so there is a gap. I'd expect these to give the same result:

    >>> ast.dump(ast.parse("print(1+2)", optimize=2))
    "Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Constant(value=3)], keywords=[]))], type_ignores=[])"
    
    
    >>> ast.dump(ast.parse(ast.parse("print(1+2)"), optimize=2))
    "Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[BinOp(left=Constant(value=1), op=Add(), right=Constant(value=2))], keywords=[]))], type_ignores=[])"
    
    Reacted by Carl Meyer
  22. carljm commented on Aug 21, 2023

    @carljm
    carljm
    on Aug 21, 2023
    Member
    More actions

    I think we should make ast.parse() optimize also when the input is an AST. At the moment there is no way to apply AST optimization to an AST that you created, so there is a gap. I'd expect these to give the same result:

    I agree. When we were discussing respecting optimize implicitly and always with PyCF_ONLY_AST, I did not think we should do this, but now that you can explicitly ask for compile to either optimize your AST or not, if you ask for it I think we should do it even on an AST input.

  23. iritkatriel
    reopened this on Aug 21, 2023
  24. iritkatriel
    self-assigned this
    on Aug 21, 2023
  25. iritkatriel
    added 2 commits that reference this issue on Aug 22, 2023

    pythongh-108113: Make it possible to optimize and AST

    b214797

    gh-108113: Make it possible to optimize an AST (#108282)

    2dfbd4f
  26. iritkatriel
    closed this as completedon Aug 23, 2023
  27. iritkatriel
    added a commit that references this issue on Dec 18, 2023

    gh-108113: [docs] mention PyCF_OPTIMIZED_AST in ast Compiler Flags (#…

    59f0766
  28. ryan-duve
    added a commit that references this issue on Dec 26, 2023

    pythongh-108113: [docs] mention PyCF_OPTIMIZED_AST in ast Compiler Fl…

    a72e2d6
  29. aisk
    added a commit that references this issue on Feb 11, 2024

    pythongh-108113: [docs] mention PyCF_OPTIMIZED_AST in ast Compiler Fl…

    db4a1f4
  30. Glyphack
    added a commit that references this issue on Sep 2, 2024

    pythongh-108113: [docs] mention PyCF_OPTIMIZED_AST in ast Compiler Fl…

    8ad4105
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @iritkatriel
    iritkatriel

Labels

type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.