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

Cached properties without triggering the cache? #1518

Closed
tamasgal opened this issue Mar 4, 2020 · 3 comments
Closed

Cached properties without triggering the cache? #1518

tamasgal opened this issue Mar 4, 2020 · 3 comments

Comments

@tamasgal
Copy link

tamasgal commented Mar 4, 2020

In a library I work on, we have several "heavy" properties which look something like this (dummy example):

import time

class Foo:
    def __init__(self):
        self._bar = None
        self._baz = None

    @property
    def bar(self):
        if self._bar is None:
            time.sleep(5)
            self._bar = 23
        return self._bar

    @property
    def baz(self):
        if self._bar is None:
            time.sleep(5)
            self._baz = 23
        return self._baz

etc. (dozens of properties).

The problem is that creating an instance and hitting TAB will evaluate all the code inside the if-blocks since Jedi sees that the values are None, which means the REPL is hanging for several seconds (up to minutes).

>>> from foo import Foo
>>> f = Foo()
>>> f.<TAB>    # hangs for a few seconds

Is there a way to e.g. tell Jedi beforehand about the signature of these properties to prevent this huge lag? I also tried using different implementations of @cached_property decorators but they have the same problems.

I also tried with type hints without success:

class Foo:
    def __init__(self):
        self._bar = None
        self._baz = None

    @property
    def bar(self) -> int:
        if self._bar is None:
            time.sleep(5)
            self._bar = 23
        return self._bar

    @property
    def baz(self) -> int:
        if self._bar is None:
            time.sleep(5)
            self._baz = 23
        return self._baz
@davidhalter
Copy link
Owner

Thanks for the report.

This is unfortunately not really a bug but a "feature". I was against adding this, but most users wanted this kind of side effect.

There's still a hidden way to toggle this, which might be good enough for you. You can say

jedi.Interpreter._allow_descriptor_getattr_default = False

That should probably do the work. (No idea how you get that properly into an IPython session).

In fact I put in a ton of effort to avoid these kind of cases and a lot of people in the community complained that their properties weren't working anymore. See also this issue: #1299 (comment)

PS: While I think you raise a valid point, I think it's an anti pattern to do a lot of calculations in properties. In general I think functions are better, because they allow you to "change" the functionality in the future while not breaking backwards compatibility. This has proven to be a big issue for Jedi in the past. So just a few thoughts (I know that's not always feasible to touch large code bases).

@krpatter-intc
Copy link
Contributor

jedi.Interpreter._allow_descriptor_getattr_default = False

Can we perhaps get this to be an official and non private setting?

@davidhalter
Copy link
Owner

Feel free to create a pull request with allow_descriptor_getattr=True for Interpreter.__init__. I will merge it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants