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 proxies #120

Closed
wants to merge 2 commits into from
Closed

Object proxies #120

wants to merge 2 commits into from

Conversation

Korijn
Copy link
Collaborator

@Korijn Korijn commented Jan 2, 2024

Closes #103

🚧🚧🚧 Work in progress

I'm just starting to get to grips with the complexity of this topic. It's been a long time since I looked at object proxying.

The boilerplate is done.

The main complexity will be in object_proxy.py. In particular the challenge is that there's no watertight method of trapping attribute access only, without also trapping method calls. So for example

class Foo:
    def __init__(self):
        self.bar = 5

    def baz(self):
        return 5

    def quux(self):
        return self.bar

f = Foo()
  • It's obvious that f.bar should be reactive.
  • However I think f.quux should not be reactive (since f.bar is already reactive).
  • f.baz is not stateful, so it doesn't need to be reactive.

The list of attributes to trap is dynamic, like the keys of a dict, but we cannot replace the internal f.__dict__. All we have to work with is a trap on f.__getattribute__ but that also fires for the f.bar and f.quux.

Another challenge is that on objects, you can define something like __iter__ to make a type iterable, while by default classes don't have an __iter__ implementation.

🤔🤔🤔💭💭💭 Needs a lot more thought...

@Korijn Korijn changed the title initial boiler plate for object proxy Object proxies Jan 2, 2024
@Korijn
Copy link
Collaborator Author

Korijn commented Jan 3, 2024

I've spent another two hours on this and am abandoning this feature (again). It just isn't possible in a generic sense. Good luck to the next guy. Problems in a nutshell:

  • proxying all attribute gets and sets generically is very complicated
    • proxying __getattribute__ and __setattr__ gets you a long way, however instance methods, static methods, class methods, instance attributes and class attributes all flow through these functions so you need a mechanism to distinguish state from logic and I couldn't find one that's foolproof
    • if you proxy __getattribute__ on Proxy, that means Proxy.__init__ will also trigger the traps but the traps rely on Proxy.__init__ to have already completed
    • magic methods must be proxied since they bypass __getattribute__
    • most magic methods (e.g. __len__) are optional on classes
  • unlike dict, list and set, there is no generic .copy() method you can rely on so detecting if something has actually changed is difficult
  • how do you traverse a class? which attributes should you traverse? how do you exclude the attributes defined on Proxy (such as target)?

... and a lot more problems

The next best alternative that does work in a generic sense is to declare a reactive dict on your class and work with that.

@Korijn Korijn closed this Jan 3, 2024
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

Successfully merging this pull request may close these issues.

Support for dataclasses and / or objects with simple attributes
1 participant