simonw / symbex

Find the Python code for specified symbols
Apache License 2.0
231 stars 6 forks source link

Support class methods #9

Closed jefftriplett closed 1 year ago

jefftriplett commented 1 year ago

Maybe I'm doing something wrong, but from the examples it looks like symbex get_queryset should work on classes too, but it doesn't appear to.

Please see:

➜ symbex --version
symbex, version 0.2.1

➜ symbex get_queryset

➜ symbex 'get_queryset'

# vs. git grep
➜ git grep get_queryset
news/admin.py:    def get_queryset(self, request):
news/admin.py:        return super().get_queryset(request).prefetch_related("tags")
news/admin.py:    def get_queryset(self, request):
news/admin.py:        return super().get_queryset(request).prefetch_related("tags")
news/managers.py:    def get_queryset(self):
news/managers.py:        return self.get_queryset().active()
news/managers.py:        return self.get_queryset().active_djangos()
news/managers.py:        return self.get_queryset().active_events()
news/managers.py:        return self.get_queryset().active_extensions()
news/managers.py:        return self.get_queryset().active_news()
news/managers.py:        return self.get_queryset().active_packages()
news/managers.py:        return self.get_queryset().active_podcasts()
news/managers.py:        return self.get_queryset().active_posts()
news/managers.py:        return self.get_queryset().active_pythons()
news/managers.py:        return self.get_queryset().active_tutorials()
news/views.py:    def get_queryset(self):
news/views.py:        recent_posts = self.get_queryset().order_by("?")[0:1]
news/views.py:    def get_queryset(self):
news/views.py:    def get_queryset(self):
news/views.py:    def get_queryset(self):
news/views.py:    def get_queryset(self):
simonw commented 1 year ago

Good call, I'd planned to handle class methods but I haven't opened an issue for that yet.

simonw commented 1 year ago

I want to do this using dot notation.

To get back the get_queryset() method of the Entry class:

symbex Entry.get_queryset

For get_queryset on all classes:

symbex '*.get_queryset'

For all get_* methods on all classes:

symbex '*.get_*'
simonw commented 1 year ago

For the first version of this I'm going to stick to one level - just methods of classes where the class is defined at the top level of the module.

simonw commented 1 year ago

The new symbex -s feature should likely output class methods too:

simonw commented 1 year ago

Spotted this while trying out symbex '*':

# File: tests/example_symbols.py Line: 70
class ClassWithMethods:
    def __init__(self, a):
        pass

    def method_types(self, b: int) -> bool:
        return True

    def method_positional_only_args(a, /, b, c):
        pass

    def method_keyword_only_args(a, *, b, c):
        pass

# File: tests/example_symbols.py Class: ClassWithMethods Line: 71
    def __init__(self, a):
        pass

# File: tests/example_symbols.py Class: ClassWithMethods Line: 74
    def method_types(self, b: int) -> bool:
        return True

# File: tests/example_symbols.py Class: ClassWithMethods Line: 77
    def method_positional_only_args(a, /, b, c):
        pass

# File: tests/example_symbols.py Class: ClassWithMethods Line: 80
    def method_keyword_only_args(a, *, b, c):
        pass

The class with its body gets output, then each method gets output again. I think it would be better not to output the methods if the class itself has been output - not sure how best to handle that.

simonw commented 1 year ago

I'm going to make the * wildcard NOT match ClassName.method - if you want all the methods you can do symbex '*.*' but that will output methods without also outputting the full classes.

simonw commented 1 year ago

Got this working:

% symbex '*.get_*' -d ../simonwillisonblog -s
# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Tag Line: 25
    def get_absolute_url(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Tag Line: 28
    def get_link(self, reltag)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Tag Line: 34
    def get_reltag(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Tag Line: 67
    def get_related_tags(self, limit=10)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Series Line: 96
    def get_absolute_url(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: BaseModel Line: 122
    def get_absolute_url(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Photoset Line: 301
    def get_absolute_url(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/models.py Class: Comment Line: 357
    def get_absolute_url(self)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/admin.py Class: BaseAdmin Line: 19
    def get_queryset(self, request)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/admin.py Class: BaseAdmin Line: 22
    def get_search_results(self, request, queryset, search_term)

# File: /Users/simon/Dropbox/Development/simonwillisonblog/blog/admin.py Class: TagAdmin Line: 68
    def get_search_results(self, request, queryset, search_term)
simonw commented 1 year ago

I'll finish this work in a PR:

jefftriplett commented 1 year ago

@simonw 🎉 danke!

I can confirm:

➜ symbex '*.get_queryset'
# File: news/admin.py Class: CategoryAdmin Line: 15
    def get_queryset(self, request):
        return super().get_queryset(request).prefetch_related("tags")

# File: news/admin.py Class: PostAdmin Line: 47
    def get_queryset(self, request):
        return super().get_queryset(request).prefetch_related("tags")

# File: news/views.py Class: PostViewSet Line: 29
    def get_queryset(self):
...