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

symtable.Class.get_methods() returns non-methods #119698

Closed
JelleZijlstra opened this issue May 29, 2024 · 20 comments
Closed

symtable.Class.get_methods() returns non-methods #119698

JelleZijlstra opened this issue May 29, 2024 · 20 comments
Labels
type-bug An unexpected behavior, bug, or error

Comments

@JelleZijlstra
Copy link
Member

JelleZijlstra commented May 29, 2024

Bug report

Bug description:

Running this file:

import symtable

st = symtable.symtable("""
class X:
    class Nested: pass
    type Alias = int
    x = (x for x in range(10))
    y = filter(lambda z: z % 2, range(10))
""", "mod", "exec")

cls = st.get_children()[0]
print(cls.get_methods())

Prints:

('Nested', 'Alias', 'genexpr', 'lambda')

None of these are methods.

@carljm and I noticed this as part of the work on PEP 649 (#119361 (comment)). My draft implementation excludes generated __annotate__ functions from the list of "methods", but perhaps all non-methods should be excluded.

CPython versions tested on:

3.12, CPython main branch

Operating systems tested on:

macOS

Linked PRs

@JelleZijlstra JelleZijlstra added the type-bug An unexpected behavior, bug, or error label May 29, 2024
@JelleZijlstra JelleZijlstra changed the title symtable.Class.get_methods() returns non-classes symtable.Class.get_methods() returns non-methods May 29, 2024
@serhiy-storchaka
Copy link
Member

The symtable CLI

$ echo "
class X:
    class Nested: pass
    type Alias = int
    x = (x for x in range(10))
    y = filter(lambda z: z % 2, range(10))
" | ./python -m symtable

shows details:

symbol table for module from file '<stdin>':
    local symbol 'X': def_local

    symbol table for class 'X':
        local symbol 'Nested': def_local
        local symbol 'Alias': def_local
        local symbol 'x': def_local
        global_implicit symbol 'range': use
        local symbol 'y': def_local
        global_implicit symbol 'filter': use

        symbol table for class 'Nested':

        symbol table for type alias 'Alias':
            free symbol '__classdict__': use
            global_implicit symbol 'int': use

        symbol table for function 'genexpr':
            local symbol '.0': def_param
            local symbol 'x': use, def_local

        symbol table for function 'lambda':
            local symbol 'z': use, def_param

We see that get_methods() returns the names of nested symbol tables. Some symbol table names like "genexpr" and "lambda" has no relation to the name of any nested symbol. It is a question why get_methods() can be useful at all.

We can simply deprecate get_methods(). Or define it as returning names of local symbols that has corresponding symbol table with type "function". Then it may be worth to add also get_classes() and get_aliases(). What do you need?

@JelleZijlstra
Copy link
Member Author

Then it may be worth to add also get_classes() and get_aliases(). What do you need?

I have never used this module and I don't know how people are using it; I ran into this problem because some tests failed while I was working on the PEP 649 implementation.

Deprecating this method seems like a reasonable path forward.

@JelleZijlstra
Copy link
Member Author

cc @picnixz in case you're interested in other problems with the symtable module.

@picnixz
Copy link
Contributor

picnixz commented Jun 4, 2024

Personally, I've never used it but I was interested in contributing to CPython so I started where I thought it was something not too hard and interesting. One use case that I have in mind is IntelliSense but I would expect those tools to actually use the AST instead (but since their code is private, I cannot say for sure).

If there is really no use-case, I'd suggest deprecating it (less things to maintain, easier for us). I've tried to search whether it's used or not on github but I've only seen this occurrence https://github.com/skulpt/skulpt/blob/8daae6db0d2834ae2e66f6407e00239d4a95ead1/skulpt.py#L101 being relevant (others are not I think). However, they are using the thing for Python 2 and are not claiming full Python 3 compatibility.

@picnixz
Copy link
Contributor

picnixz commented Jun 6, 2024

I've implemented two suggestions so feel free to pick either of the two.

I decided to keep the PR fixing the interface since Carl and Jelle have reviewed it and the work was not that much.

@picnixz
Copy link
Contributor

picnixz commented Jun 12, 2024

For the record, I've implemented get_aliases and get_classes using a more generic approach in a separate branch: https://github.com/picnixz/cpython/tree/feat-symtable-classes

Note that the branch actually contains a local copy of #120151 (and I will not open a PR yet).

@JelleZijlstra
Copy link
Member Author

I would only add such methods if someone comes up with an actual use case.

@serhiy-storchaka
Copy link
Member

I only have found the use of this method in the skulpt project, which is Python 2 only. I also prefer to deprecate it.

@picnixz
Copy link
Contributor

picnixz commented Jun 12, 2024

To summarize, we would fix the method in 3.14 (and 3.12, 3.13, depending on the backport decision), deprecate it in 3.15 and remove it in 3.17? (and it's fine that we don't need the other methods)

@serhiy-storchaka
Copy link
Member

Why not deprecate it in 3.14?

@picnixz
Copy link
Contributor

picnixz commented Jun 12, 2024

Err..., maybe someone will be interested in using it since we fixed it? (I can add the deprecation in the same or a follow-up PR though)

@JelleZijlstra
Copy link
Member Author

We should definitely do only one of deprecating and fixing, not both; it doesn't make sense to radically change the implementation of the method and then immediately deprecate it.

I would prefer to fix the method instead of deprecating it though. We have a solution that fixes the method (#120151), and I don't think maintaining the method in the future will be very costly. I think we should only deprecate the method if there's some strong reason we don't want it in the library in the future.

@serhiy-storchaka
Copy link
Member

If we are going to fix get_methods(), we should also add get_classes(), get_aliases(), etc in the Class class. Then add the SymbolTable subclass Module with methods get_classes(), get_functions(), etc. Then perhaps add get_classes() and get_functions() also in the Function class, for local classes and functions.

Note that currently functions which return a limited set of names, such as get_parameters() and get_locals(), only filter names of symbols by their scope and flags. New get_methods(), get_classes(), etc, will be more complex, they need to look into two tables: symbols and symboltables. Too complex behavior for a feature that no one will possibly use.

JelleZijlstra added a commit that referenced this issue Jun 20, 2024
JelleZijlstra pushed a commit to JelleZijlstra/cpython that referenced this issue Jun 20, 2024
…haviour correctly (python#120151)

Co-authored-by: Jelle Zijlstra <[email protected]>
(cherry picked from commit b8a8e04)
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Jun 20, 2024
… its behaviour correctly (pythonGH-120151)

(cherry picked from commit b8a8e04)

Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Jun 20, 2024
… its behaviour correctly (pythonGH-120151)

(cherry picked from commit b8a8e04)

Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Jun 20, 2024
JelleZijlstra added a commit that referenced this issue Jun 20, 2024
…ehaviour correctly (#120151) (#120776)

(cherry picked from commit b8a8e04)

Co-authored-by: Bénédikt Tran <[email protected]>
JelleZijlstra added a commit that referenced this issue Jun 20, 2024
…ehaviour correctly (GH-120151) (#120777)

(cherry picked from commit b8a8e04)


Co-authored-by: Bénédikt Tran <[email protected]>
mrahtz pushed a commit to mrahtz/cpython that referenced this issue Jun 30, 2024
mrahtz pushed a commit to mrahtz/cpython that referenced this issue Jun 30, 2024
@serhiy-storchaka
Copy link
Member

I still prefer to deprecate this method. But it is fine if other core developers need it.

@JelleZijlstra
Copy link
Member Author

I'm not strongly opposed to deprecating the method, but we now have a fixed implementation and I don't foresee the method causing a lot of problems in the future, so I don't see a strong cause for inconveniencing users by deprecating it.

@serhiy-storchaka
Copy link
Member

It is pretty complex now, with complex tests and documentation, and I am not even sure that it is correct. It has high maintenance cost, and its usefulness is questionable.

Actually, I just found a bug in it:

@serhiy-storchaka
Copy link
Member

import symtable
st = symtable.symtable("""
class X:
    x = (x for x in range(10))
    genexpr = 1
""", '?', 'exec')
cls = st.get_children()[0]
print(cls.get_methods())

(For some reasons Firefox crashed on my computer when paste from clipboard. After updating it this was fixed.)

@picnixz
Copy link
Contributor

picnixz commented Jul 10, 2024

Ah I think I forgot that case where a symbol is called 'genexpr'. Actually, I also have this issue:

import symtable
st = symtable.symtable("""
class X:
    genexpr = (x for x in range(10))
""", '?', 'exec')
cls = st.get_children()[0]
print(cls.get_methods())

Ideally, if we could rename 'genexpr' by '.genexpr', it could make the name special. Because otherwise the following 'genexpr' would not be picked up... (I cannot just say that the identifier is invalid, for 'lambda' I can since it's a keyword but not for 'genexpr').

class X:
	def genexpr(): pass

@picnixz
Copy link
Contributor

picnixz commented Jul 17, 2024

Re-opening until the fix of the fix (+backports) lands and the deprecation notice is merged.

@picnixz picnixz reopened this Jul 17, 2024
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 17, 2024
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 17, 2024
JelleZijlstra pushed a commit that referenced this issue Jul 17, 2024
@serhiy-storchaka
Copy link
Member

I opened also #121914.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants