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

Object representation in Variable Explorer #310

Open
jpcanal opened this issue Aug 21, 2021 · 8 comments
Open

Object representation in Variable Explorer #310

jpcanal opened this issue Aug 21, 2021 · 8 comments

Comments

@jpcanal
Copy link

jpcanal commented Aug 21, 2021

Problem Description

In the variable explorer, non-standard objects value is redundant with type. __str__ or __repr__ result would be expected to be seen, which usually carries useful information about the object.
Standard Python objects are cleared for representation visualization, but not any object (comment about this on spyder-ide/spyder#1867).
A performance issue is mentioned in the link, but I think PyCharm does show this representation for custom objects.

What steps reproduce the problem?

  1. Create custom object, for example:
class Vector:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
    def __str__(self):
        return "Vector (x=%f, y=%f, z=%f)" % (self.x, self.y, self.z)

vect = Vector(1,2,3)
  1. See variable vect in Variable Explorer. Type is Vector and Value is Vector object of __main__ module.

What is the expected output? What do you see instead?

Value column is expected to be populated with Vector (x=1.000000, y=2.000000, z=3.000000) or similar information about the object, as print(obj) would show.

Versions

  • Spyder version: 5.1.1
  • Python version: 3.7.9 64 bits
  • Qt version: 5.12.10
  • PyQt version: 5.12.3
  • Operating System name/version: Windows 10
@ccordoba12
Copy link
Member

Hey @jpcanal, thanks for reporting. This needs to be addressed in spyder-kernels, not here, so I'm moving your issue there.

@ccordoba12 ccordoba12 transferred this issue from spyder-ide/spyder Aug 22, 2021
@ccordoba12
Copy link
Member

ccordoba12 commented Aug 22, 2021

The problem we found a while ago is that slow or badly implemented repr's bring down the console down. That's because the kernel needs to wait until the repr of all elements in the namespace is computed before showing the next prompt.

So we preferred to only trust in the repr's of basic or well known objects and use a generic one for the rest. The improvement you're asking for would involve trying to get the repr declared by an object and if it fails to return in less than 0.1 seconds or so, then use our generic one.

For that we could use one of the solutions described in this SO post:

https://stackoverflow.com/questions/366682/how-to-limit-execution-time-of-a-function-call

In particular, I really like this one:

https://github.com/kata198/func_timeout

@ccordoba12
Copy link
Member

ccordoba12 commented Aug 22, 2021

The only problem with that is that a way to run a function until a timeout is reached that works in all operating systems involves using threads. And I don't know if that could work with the rest of our architecture.

@impact27, what do you think about this?

@jpcanal
Copy link
Author

jpcanal commented Aug 22, 2021

I guess a mid-term solution would be to add an option on the right side menu, perhaps? In the same way that you added "Show arrays min/max", perhaps an option for "Retrieve objects representation" could be added?

Disabled by default, the option may be enabled by the user. If the console starts to lag, the user can disable it and get back the generic description.

I don't know in which case you saw the knock down, but I wouldn't expect a problem for most "simple" objects...

However, even in the default description case, I see it a bit too generic... I mean, PyCharm default description is something like <__main__.dummy object at 0x00000201A8CBCC70> for an object that does not have either __repr__ nor __str__ methods. In this case at least you can understand if two objects in a list are the same or not (because of the memory address difference). I guess this is built by the variable explorer if hasattr() returned False for those methods, so it is code that you control and is just a trivial string replacement operation. In this way, this representation adds some more value to the user.

@impact27
Copy link
Contributor

Or we could have a thread to compute the reps?

@ccordoba12
Copy link
Member

ccordoba12 commented Aug 23, 2021

perhaps an option for "Retrieve objects representation" could be added?

I disagree with that for the following reasons:

  • That's designing for specific use cases, not for the general audience that use Spyder. If people don't know what a repr is (and who does unless you're an intermediate or advanced Python user), then it'd be very hard to explain to them what that option does.
  • Once we add an option for that, it'd be very hard to remove it afterwards (people find out about options in StackOverflow or our issue tracker and then are surprised when they are gone).

I don't know in which case you saw the knock down, but I wouldn't expect a problem for most "simple" objects...

Numpy repr's were terribly inefficient some years ago (I don't know if that has improved).

But simply having this

class Foo
    def __repr___(self):
        import time
        time.sleep(100)

foo = Foo()

will block the console for 100 seconds. So we can't trust generic repr's unless we're able to prevent that.

However, even in the default description case, I see it a bit too generic

We decided to do that so that our descriptions make sense for people that don't know that <__main__.dummy object at 0x00000201A8CBCC70> is making reference to the memory address of dummy. How on earth is a newbie supposed to know that?

However, I agree that it'd be nice to add an option to show that info for power users (disabled by default, of course).

@ccordoba12
Copy link
Member

ccordoba12 commented Aug 23, 2021

Or we could have a thread to compute the reps?

@impact27, the problem with that is that the Variable Explorer would be out of sync with the console namespace, even more so for slow to compute repr's.

So I prefer to leave this call to be blocking and adding a timeout to the method or function that tries to read the repr of an object.

@jpcanal
Copy link
Author

jpcanal commented Aug 24, 2021

I agree with you on the best solution: time-out thread. I guess it is also the most complex...

However, I agree that it'd be nice to add an option to show that info for power users (disabled by default, of course).

I don't know if would call them power users... I would guess most users positively appreciate Variable Explorer visual approach over bare interpreter, and that is one of the reasons for their preference.

You can find examples in the same standard library, e.g.:

from enum import Enum

class Color(Enum):
    RED = 1
    BLUE = 2

clr = Color.RED

where you get Color object of __main__ module, instead of '<Color.RED: 1>' or Color.RED for clr...

Besides, I would consider the same improvement would apply to the Object Explorer too, for the value of the object. Here I understand the limitation is not lagging, as @propertyes are shown, and I've seen nastier things in properties than in __repr__() or __str__().

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