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

Considering accessors versus properties on models. #105

Closed
ghost opened this issue Aug 30, 2020 · 13 comments
Closed

Considering accessors versus properties on models. #105

ghost opened this issue Aug 30, 2020 · 13 comments
Labels
blocked This issue cannot yet be completed, or this PR is not yet ready to merge because of a dependency enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed optimization Optimizations to the code priority This should be worked on before anything else
Projects
Milestone

Comments

@ghost
Copy link

ghost commented Aug 30, 2020

I would like to move most properties on models across to use accessor methods instead of properties. This will probably also involve making most fields private and creating getters for them.

The reason for this is that it allows me to hide whether an operation is using a cached value on the model or whether it is performing a cache lookup behind the method interface. As well as improving consistency elsewhere, it will ensure that introspecting a model instance does not perform a lot of slow operations based on the execution of properties that make cache calls internally.

This would either cover changing just cache calls, or changing all field accessions:

event.channel.id -> event.get_channel().get_id()

This will require at least a partial rewrite of most model-bound tests.

I am still 50-50 as to whether I want to go ahead with this, since it will make stuff more verbose for people switching from other frameworks, and may be considered to be messy, but at the same time the inconsistency between "what is a field and what is a method" which may be messy currently due to Discords ™️ design should be reduced/removed.

Using accessors in Python isn't considered an antipattern per-se either. Many modules follow that approach.

Links to https://discordapp.com/channels/574921006817476608/577602779410071575/749746748615950539

@ghost ghost added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers optimization Optimizations to the code labels Aug 30, 2020
@ghost ghost added this to To do in v2.0.0 via automation Aug 30, 2020
@davfsa davfsa added the priority This should be worked on before anything else label Aug 30, 2020
@ghost ghost added the blocked This issue cannot yet be completed, or this PR is not yet ready to merge because of a dependency label Aug 30, 2020
@ghost ghost self-assigned this Aug 30, 2020
@FasterSpeeding
Copy link
Collaborator

Do you have any idea how this would work with builder cache objects? And would we maybe have a method that returns a dict of the raw values or something like that and is on all models?

@ghost
Copy link
Author

ghost commented Aug 30, 2020

Do you have any idea how this would work with builder cache objects? And would we maybe have a method that returns a dict of the raw values or something like that and is on all models?

You'd probably have to look at private fields and remove the underscore directly for the constructor to work around this.

@FasterSpeeding
Copy link
Collaborator

FasterSpeeding commented Aug 30, 2020

Do you have any idea how this would work with builder cache objects? And would we maybe have a method that returns a dict of the raw values or something like that and is on all models?

You'd probably have to look at private fields and remove the underscore directly for the constructor to work around this.

If you're fine with looking at private fields then it shouldn't be too hard to convert the data class over then

v2.0.0 automation moved this from To do to Done Aug 30, 2020
@davfsa davfsa reopened this Aug 30, 2020
v2.0.0 automation moved this from Done to In progress Aug 30, 2020
@davfsa davfsa moved this from In progress to To do in v2.0.0 Aug 30, 2020
@ghost
Copy link
Author

ghost commented Aug 30, 2020

Do you have any idea how this would work with builder cache objects? And would we maybe have a method that returns a dict of the raw values or something like that and is on all models?

You'd probably have to look at private fields and remove the underscore directly for the constructor to work around this.

If you're fine with looking at private fields then it shouldn't be too hard to convert the data class over then

It is internal to the library. As long as we do not encourage users to do this, we shouldn't have a problem.

Fields are exposed on the assumption we are responsible developers, tests should highlight bad stuff :^)

@Tmpod
Copy link
Contributor

Tmpod commented Aug 30, 2020

I feel like the cons outweight the pros quite a bit in this case. Python is a syntatically simple language in general, and the ease of use is one of its key features, and certainly one that attracts a lot of people. Making the library super verbose by making everything a call to a get_foo() method will substantially reduce the simplicity and pain-less aspect of this library, which I'm afraid will make people gravitate towards solutions like discord.py more in the future. Even though Hikari is, in my opinion, miles better than discord.py due to its fully typed, documented and tested nature, I'm under the impression that it would level out, or even make Hikari less desireable, as its nice pros would be muffled by the high verbosity and "ugliness" of code using it.
I see how this is a quite complicated decision however. Having these calls would simplify stuff and make everything more consistent... You say it's not an anti-pattern per-se and that many modules use it, but I in all honesty, I fail to recall any. Could you point me some?

Thanks 🅱️iscord

@ghost
Copy link
Author

ghost commented Aug 30, 2020

You say it's not an anti-pattern per-se and that many modules use it, but I in all honesty, I fail to recall any. Could you point me some?

Pretty much all standard lib modules like asyncio, thread, logging, do this.

Mainly they use future.result() instead of future.get_result() but the concept is the same.

If people are happy that you have to be mindful of how you use event entities, for example, then I am fine with keeping stuff how it is.

An alternative to this may be using custom overrides of __dir__ to exclude properties on instances.

This fixes the introspection issue by allowing inspect.getmembers to skip stuff we deem slow. We could follow what Python's enum type does, which is only specify specific values (in this case slots only, or attrs fields only).

>>>
>>> class Foo:
...     def bar(self): pass
...     def baz(self): pass
...     def bork(self): pass
...     def __dir__(self): return ["bar", "baz"]
...
>>> import inspect
>>>
>>> inspect.getmembers(Foo())
[('bar', <bound method Foo.bar of <__main__.Foo object at 0x7395e7afa0>>), ('baz', <bound method Foo.baz of <__main__.Foo object at 0x7395e7afa0>>)]
# notice no bork

@ghost ghost changed the title Conversion of properties to accessor methods. Considering accessors versus properties on models. Aug 31, 2020
@ghost
Copy link
Author

ghost commented Sep 1, 2020

Might close this for the time being and reopen it if I still feel it is relevant.

@ghost ghost closed this as completed Sep 1, 2020
v2.0.0 automation moved this from To do to Done Sep 1, 2020
@ghost ghost added this to the 2.0.0 milestone Sep 4, 2020
@ghost ghost reopened this Sep 9, 2020
v2.0.0 automation moved this from Done to To do Sep 9, 2020
@ghost
Copy link
Author

ghost commented Sep 9, 2020

@FasterSpeeding @davfsa please.

If we are going ahead with this, we need to look into the pros and cons of doing this and ensure we have it noted down before we make a bad decision.

@davfsa
Copy link
Member

davfsa commented Sep 9, 2020

As I said through Discord on the day this was opened (should have done it through here) I dont really mind one or another. Its true that properties might be a more "pythonish" approach.

I think its fine to have get_x in a lot of cases (eg, get_channel or get_guild). But for simple or inmutable arguments that wont change (eg id) I think its fine to have them as properties.

@ghost
Copy link
Author

ghost commented Sep 9, 2020

I think its fine to have get_x in a lot of cases (eg, get_channel or get_guild). But for simple or inmutable arguments that wont change (eg id) I think its fine to have them as properties.

How would you suggest handling events and inheritance hierarchy in this case?

Consider GuildEvent which has a guild property.

Both GuildAvailable and GuildUnavailable provide this attribute in different ways. The former is always aware of the field directly as it is part of the low-level event payload, whereas the latter has to do a cache call for this (calls the GuildEvent default property).

Should these be properties, getter methods, or just remove this from the base class altogether and provide .guild on an available event and .get_guild() on an unavailable event, in your opinion?

@ghost ghost pinned this issue Sep 9, 2020
@ghost ghost assigned FasterSpeeding and davfsa and unassigned ghost Sep 9, 2020
@davfsa
Copy link
Member

davfsa commented Sep 10, 2020

Should these be properties, getter methods, or just remove this from the base class altogether and provide .guild on an available event and .get_guild() on an unavailable event, in your opinion?

In this case I would use get_guild() for both.

I probably did not explain myself correctly before. I meant if its something that we can assure will always be there (eg id) and we dont have to make any calls for it in any variant of the object, then its a property, else, a get_x. That sound better?

Edit: this might cause a lot of inconsistencies. Tbh, i would first do #49 and try to get them as simple as we can to later implement this

@ghost
Copy link
Author

ghost commented Sep 10, 2020

@davfsa suggestions on what that involves?

@ghost
Copy link
Author

ghost commented Sep 16, 2020

For now I think we have avoided the issue, so will close.

Will reopen if/when this is needed.

@ghost ghost closed this as completed Sep 16, 2020
v2.0.0 automation moved this from To do to Done Sep 16, 2020
@ghost ghost unpinned this issue Sep 16, 2020
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked This issue cannot yet be completed, or this PR is not yet ready to merge because of a dependency enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed optimization Optimizations to the code priority This should be worked on before anything else
Projects
v2.0.0
  
Done
Development

No branches or pull requests

3 participants