Trouble with "decorator mixins" and ABCs
Hey all, I recently ran into some trouble and that I think deserves some attention. Consider the following case: class A(ABC): @abstractmethod def __lt__(self, other): pass @dataclass(order=True) class B(A): x: int = 0 class C(B): pass Although B technically implements A's abstract methods, it is still considered an abstract class, and calling B() will fail. However, C is considered a non-abstract class and calling C() will succeed. This is because a class's "abstraction" is decided when a class is created. When B is created it does not implement __lt__, the implementation is added after creation by the dataclass decorator. Since C inherits from B after having its __lt__ set, C is considered non-abstract. This will also affect any "mixin-decorator" such as total_ordering, and also (a less-preferred use case) implementing abstract methods after class creation. There are many solutions for this, my proposed solution is for abc.ABCMeta to implement a __setattr__, that erases unimplemented metaclasses f they are implemented after class creation, Something along these lines: class ABCMeta2(ABCMeta): def __setattr__(self, key, value): super().__setattr__(key, value) if key != '__abstractmethods__' and value is not None: self.__abstractmethods__ -= {key} Obviously some additional logic is needed to handle sub-classes and such, but I think it's a good starting point. Any thoughts?
Interesting problem. Since the solution is just updating `__abstractmethods__`, this could be done in the `@dataclass` decorator, no new ABC needed. On Thu, Sep 24, 2020 at 8:32 AM Ben Avrahami <avrahami.ben@gmail.com> wrote:
Hey all, I recently ran into some trouble and that I think deserves some attention. Consider the following case:
class A(ABC): @abstractmethod def __lt__(self, other): pass
@dataclass(order=True) class B(A): x: int = 0
class C(B): pass
Although B technically implements A's abstract methods, it is still considered an abstract class, and calling B() will fail. However, C is considered a non-abstract class and calling C() will succeed. This is because a class's "abstraction" is decided when a class is created. When B is created it does not implement __lt__, the implementation is added after creation by the dataclass decorator. Since C inherits from B after having its __lt__ set, C is considered non-abstract. This will also affect any "mixin-decorator" such as total_ordering, and also (a less-preferred use case) implementing abstract methods after class creation.
There are many solutions for this, my proposed solution is for abc.ABCMeta to implement a __setattr__, that erases unimplemented metaclasses f they are implemented after class creation, Something along these lines: class ABCMeta2(ABCMeta): def __setattr__(self, key, value): super().__setattr__(key, value) if key != '__abstractmethods__' and value is not None: self.__abstractmethods__ -= {key}
Obviously some additional logic is needed to handle sub-classes and such, but I think it's a good starting point.
Any thoughts? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6BNJ3Y... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
Does anyone know if attrs handles this? I don't have a recent version installed, but I'll take a look later today. Eric On 9/24/2020 11:59 AM, Guido van Rossum wrote:
Interesting problem. Since the solution is just updating `__abstractmethods__`, this could be done in the `@dataclass` decorator, no new ABC needed.
On Thu, Sep 24, 2020 at 8:32 AM Ben Avrahami <avrahami.ben@gmail.com <mailto:avrahami.ben@gmail.com>> wrote:
Hey all, I recently ran into some trouble and that I think deserves some attention. Consider the following case:
class A(ABC): @abstractmethod def __lt__(self, other): pass
@dataclass(order=True) class B(A): x: int = 0
class C(B): pass
Although B technically implements A's abstract methods, it is still considered an abstract class, and calling B() will fail. However, C is considered a non-abstract class and calling C() will succeed. This is because a class's "abstraction" is decided when a class is created. When B is created it does not implement __lt__, the implementation is added after creation by the dataclass decorator. Since C inherits from B after having its __lt__ set, C is considered non-abstract. This will also affect any "mixin-decorator" such as total_ordering, and also (a less-preferred use case) implementing abstract methods after class creation.
There are many solutions for this, my proposed solution is for abc.ABCMeta to implement a __setattr__, that erases unimplemented metaclasses f they are implemented after class creation, Something along these lines: class ABCMeta2(ABCMeta): def __setattr__(self, key, value): super().__setattr__(key, value) if key != '__abstractmethods__' and value is not None: self.__abstractmethods__ -= {key}
Obviously some additional logic is needed to handle sub-classes and such, but I think it's a good starting point.
Any thoughts? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6BNJ3Y... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido <http://python.org/~guido>) /Pronouns: he/him //(why is my pronoun here?)/ <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
On Thu, Sep 24, 2020 at 7:02 PM Eric V. Smith <eric@trueblade.com> wrote:
Does anyone know if attrs handles this? I don't have a recent version installed, but I'll take a look later today.
attrs handles this only if you set slots=True (which makes sense since attrs would have to rebuild the class)
I think it can be easily solved much like a normal bug fix - upon instance creation and before throwing an "ABC doesn't implement..." error, recheck the class __dict__ or even mro using hasattr, and only then throw the exception. It will slow down only the specific course where the methods were defined dynamically. Plus it'll support dynamic __getattr__ on a child metaclass as an added bonus. Bar Harel. On Thu, Sep 24, 2020, 7:28 PM Ben Avrahami <avrahami.ben@gmail.com> wrote:
On Thu, Sep 24, 2020 at 7:02 PM Eric V. Smith <eric@trueblade.com> wrote:
Does anyone know if attrs handles this? I don't have a recent version installed, but I'll take a look later today.
attrs handles this only if you set slots=True (which makes sense since attrs would have to rebuild the class) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KLMQ7H... Code of Conduct: http://python.org/psf/codeofconduct/
I'd like to bring another insight to the table: According to the pep, "Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported." The notice exists both in the pep and at the abc module's docs, and is exactly what this idea is all about. My question is why? Was there an objection for implementing such a thing, or was it complex enough to postpone for the time being? On Tue, Sep 29, 2020 at 2:51 PM Bar Harel <bzvi7919@gmail.com> wrote:
I think it can be easily solved much like a normal bug fix - upon instance creation and before throwing an "ABC doesn't implement..." error, recheck the class __dict__ or even mro using hasattr, and only then throw the exception.
It will slow down only the specific course where the methods were defined dynamically. Plus it'll support dynamic __getattr__ on a child metaclass as an added bonus.
Bar Harel.
On Thu, Sep 24, 2020, 7:28 PM Ben Avrahami <avrahami.ben@gmail.com> wrote:
On Thu, Sep 24, 2020 at 7:02 PM Eric V. Smith <eric@trueblade.com> wrote:
Does anyone know if attrs handles this? I don't have a recent version installed, but I'll take a look later today.
attrs handles this only if you set slots=True (which makes sense since attrs would have to rebuild the class) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KLMQ7H... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Sep 29, 2020 at 4:53 PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the table: According to the pep, "Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported."
The notice exists both in the pep and at the abc module's docs, and is exactly what this idea is all about.
My question is why? Was there an objection for implementing such a thing, or was it complex enough to postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not supposed to change throughout its lifetime. The problem arises when decorators change the class before it's ever used, as that should still be considered its "initialization" as far as the user is concerned, so its "abstract-ness" should change accordingly. I agree with Guido in that the cleanest solution is to change the decorators. Perhaps a module function to recalculate a class's __abstractmethods __, that mixin decorators can call prior to returning the class. I can't really think of standard library class decorators that would use this global function other than `dataclass` and `total_ordering`, but in this way, 3rd party decorators can easily support implementing abstract functions. The one downside to this solution is that it can easily be abused by calling it in the middle of a class's lifetime.
On 9/29/2020 10:49 AM, Ben Avrahami wrote:
On Tue, Sep 29, 2020 at 4:53 PM Bar Harel <bzvi7919@gmail.com <mailto:bzvi7919@gmail.com>> wrote:
I'd like to bring another insight to the table: According to the pep, "Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported."
The notice exists both in the pep and at the abc module's docs, and is exactly what this idea is all about.
My question is why? Was there an objection for implementing such a thing, or was it complex enough to postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not supposed to change throughout its lifetime. The problem arises when decorators change the class before it's ever used, as that should still be considered its "initialization" as far as the user is concerned, so its "abstract-ness" should change accordingly. I agree with Guido in that the cleanest solution is to change the decorators. Perhaps a module function to recalculate a class's __abstractmethods __, that mixin decorators can call prior to returning the class. I can't really think of standard library class decorators that would use this global function other than `dataclass` and `total_ordering`, but in this way, 3rd party decorators can easily support implementing abstract functions. The one downside to this solution is that it can easily be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as to keep it easy to use. But I can be argued with: it's not like a beginner would stumble over this issue. Eric
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable. On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami wrote:
On Tue, Sep 29, 2020 at 4:53 PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the table: According to the pep, "Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported."
The notice exists both in the pep and at the abc module's docs, and is exactly what this idea is all about.
My question is why? Was there an objection for implementing such a thing, or was it complex enough to postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not supposed to change throughout its lifetime. The problem arises when decorators change the class before it's ever used, as that should still be considered its "initialization" as far as the user is concerned, so its "abstract-ness" should change accordingly. I agree with Guido in that the cleanest solution is to change the decorators. Perhaps a module function to recalculate a class's __abstractmethods __, that mixin decorators can call prior to returning the class. I can't really think of standard library class decorators that would use this global function other than `dataclass` and `total_ordering`, but in this way, 3rd party decorators can easily support implementing abstract functions. The one downside to this solution is that it can easily be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as to keep it easy to use. But I can be argued with: it's not like a beginner would stumble over this issue.
Eric _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE... Code of Conduct: http://python.org/psf/codeofconduct/
Would it help if ‘__abstractmethods__’ was documented, overruling whatever the PEP says? On Wed, Sep 30, 2020 at 00:01 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable.
On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami
wrote:
On Tue, Sep 29, 2020 at 4:53
PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the
table: According to the pep, "Dynamically
adding abstract methods to a class, or attempting to
modify the abstraction status of a method or class once
it is created, are not supported."
The notice exists both in the pep and at the abc module's
docs, and is exactly what this idea is all about.
My question is why? Was there an objection for
implementing such a thing, or was it complex enough to
postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not
supposed to change throughout its lifetime. The problem
arises when decorators change the class before it's ever
used, as that should still be considered its
"initialization" as far as the user is concerned, so
its "abstract-ness" should change accordingly.
I agree with Guido in that the cleanest solution is to
change the decorators. Perhaps a module function to
recalculate a class's __abstractmethods __, that mixin
decorators can call prior to returning the class. I can't
really think of standard library class decorators that would
use this global function other than `dataclass` and
`total_ordering`, but in this way, 3rd party decorators can
easily support implementing abstract functions.
The one downside to this solution is that it can easily
be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as
to keep it easy to use. But I can be argued with: it's not like a
beginner would stumble over this issue.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ASPJKU...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
I suppose it's better than nothing, since right now libraries are (rightfully) afraid to modify it or rely on its behaviour. Although I expect this will lead to a lot of repeated code, that looks a lot like the `__abstractmethods__` population in `_py_abc`. I think it's preferable to ease abstract method re-population for 3rd (and 1st) party libraries, but the dev team probably knows better. If this is an accepted solution, I'd be happy to write a BPO and PR. On Wed, Sep 30, 2020 at 5:30 PM Guido van Rossum <guido@python.org> wrote:
Would it help if ‘__abstractmethods__’ was documented, overruling whatever the PEP says?
On Wed, Sep 30, 2020 at 00:01 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable.
On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami
wrote:
On Tue, Sep 29, 2020 at 4:53
PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the
table: According to the pep, "Dynamically
adding abstract methods to a class, or attempting to
modify the abstraction status of a method or class once
it is created, are not supported."
The notice exists both in the pep and at the abc module's
docs, and is exactly what this idea is all about.
My question is why? Was there an objection for
implementing such a thing, or was it complex enough to
postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not
supposed to change throughout its lifetime. The problem
arises when decorators change the class before it's ever
used, as that should still be considered its
"initialization" as far as the user is concerned, so
its "abstract-ness" should change accordingly.
I agree with Guido in that the cleanest solution is to
change the decorators. Perhaps a module function to
recalculate a class's __abstractmethods __, that mixin
decorators can call prior to returning the class. I can't
really think of standard library class decorators that would
use this global function other than `dataclass` and
`total_ordering`, but in this way, 3rd party decorators can
easily support implementing abstract functions.
The one downside to this solution is that it can easily
be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as
to keep it easy to use. But I can be argued with: it's not like a
beginner would stumble over this issue.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ASPJKU...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
That might work (though not for older Python versions). What kind of API were you thinking of for adding and deleting abstract methods? On Thu, Oct 1, 2020 at 01:07 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I suppose it's better than nothing, since right now libraries are (rightfully) afraid to modify it or rely on its behaviour. Although I expect this will lead to a lot of repeated code, that looks a lot like the `__abstractmethods__` population in `_py_abc`. I think it's preferable to ease abstract method re-population for 3rd (and 1st) party libraries, but the dev team probably knows better. If this is an accepted solution, I'd be happy to write a BPO and PR.
On Wed, Sep 30, 2020 at 5:30 PM Guido van Rossum <guido@python.org> wrote:
Would it help if ‘__abstractmethods__’ was documented, overruling whatever the PEP says?
On Wed, Sep 30, 2020 at 00:01 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable.
On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami
wrote:
On Tue, Sep 29, 2020 at 4:53
PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the
table: According to the pep, "Dynamically
adding abstract methods to a class, or attempting to
modify the abstraction status of a method or class once
it is created, are not supported."
The notice exists both in the pep and at the abc module's
docs, and is exactly what this idea is all about.
My question is why? Was there an objection for
implementing such a thing, or was it complex enough to
postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not
supposed to change throughout its lifetime. The problem
arises when decorators change the class before it's ever
used, as that should still be considered its
"initialization" as far as the user is concerned, so
its "abstract-ness" should change accordingly.
I agree with Guido in that the cleanest solution is to
change the decorators. Perhaps a module function to
recalculate a class's __abstractmethods __, that mixin
decorators can call prior to returning the class. I can't
really think of standard library class decorators that would
use this global function other than `dataclass` and
`total_ordering`, but in this way, 3rd party decorators can
easily support implementing abstract functions.
The one downside to this solution is that it can easily
be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as
to keep it easy to use. But I can be argued with: it's not like a
beginner would stumble over this issue.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ASPJKU...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
--
--Guido (mobile)
I have a suggestion for an API, how about a keyword argument to "abstractmethod" that allows to set the abstract method as "optional" meaning that sub-implementations can either implement their own version or use the ABC's own, infact, I think the STDLIB could use this itself (I haven't looked at the stdlib entirely). For native code, a new method flag would be required, probably something like "OPTIONAL_ABSTRACT" (I forget the naming convention for flags) example: @abstractmethod(True) # Method won't be included in required implementations. Default: False (for bcompat reasons)
On Thu, Oct 1, 2020 at 7:51 AM William Pickard <lollol222gg@gmail.com> wrote:
I have a suggestion for an API, how about a keyword argument to "abstractmethod" that allows to set the abstract method as "optional" meaning that sub-implementations can either implement their own version or use the ABC's own, infact, I think the STDLIB could use this itself (I haven't looked at the stdlib entirely).
For native code, a new method flag would be required, probably something like "OPTIONAL_ABSTRACT" (I forget the naming convention for flags)
example: @abstractmethod(True) # Method won't be included in required implementations. Default: False (for bcompat reasons)
That's already possible: just don't use @abstractmethod at all. You can still override! -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
In theory, all that's necessary is to just run `_py_abc`'s abstract method population again (`ABCMeta.__new__`, with slight adjustments): def update_abstractmethods(cls): if not isinstance(cls, ABCMeta): return cls abstracts = set() # include only abstract methods that are not implemented for name in cls.__abstractmethods__: value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) # check for new abstract methods # note that for most cases this loop is useless and can be scrapped for a minimal solution for name, value in cls.__dict__.items(): if name not in abstracts \ and getattr(value, "__isabstractmethod__", False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) return cls My preferred API will be to just add this function into `abc`, but the minimal solution is to just airdrop this behaviour into `dataclass` (and possibly `total_ordering`), and let 3rd parties implement this logic themselves. On Thu, Oct 1, 2020 at 5:08 PM Guido van Rossum <guido@python.org> wrote:
That might work (though not for older Python versions). What kind of API were you thinking of for adding and deleting abstract methods?
On Thu, Oct 1, 2020 at 01:07 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I suppose it's better than nothing, since right now libraries are (rightfully) afraid to modify it or rely on its behaviour. Although I expect this will lead to a lot of repeated code, that looks a lot like the `__abstractmethods__` population in `_py_abc`. I think it's preferable to ease abstract method re-population for 3rd (and 1st) party libraries, but the dev team probably knows better. If this is an accepted solution, I'd be happy to write a BPO and PR.
On Wed, Sep 30, 2020 at 5:30 PM Guido van Rossum <guido@python.org> wrote:
Would it help if ‘__abstractmethods__’ was documented, overruling whatever the PEP says?
On Wed, Sep 30, 2020 at 00:01 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable.
On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami
wrote:
On Tue, Sep 29, 2020 at 4:53
PM Bar Harel <bzvi7919@gmail.com> wrote:
I'd like to bring another insight to the
table: According to the pep, "Dynamically
adding abstract methods to a class, or attempting to
modify the abstraction status of a method or class once
it is created, are not supported."
The notice exists both in the pep and at the abc module's
docs, and is exactly what this idea is all about.
My question is why? Was there an objection for
implementing such a thing, or was it complex enough to
postpone for the time being?
I think this is fine. The "abstract-ness" of a class is not
supposed to change throughout its lifetime. The problem
arises when decorators change the class before it's ever
used, as that should still be considered its
"initialization" as far as the user is concerned, so
its "abstract-ness" should change accordingly.
I agree with Guido in that the cleanest solution is to
change the decorators. Perhaps a module function to
recalculate a class's __abstractmethods __, that mixin
decorators can call prior to returning the class. I can't
really think of standard library class decorators that would
use this global function other than `dataclass` and
`total_ordering`, but in this way, 3rd party decorators can
easily support implementing abstract functions.
The one downside to this solution is that it can easily
be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as
to keep it easy to use. But I can be argued with: it's not like a
beginner would stumble over this issue.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ASPJKU...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
--
--Guido (mobile)
Hm, having a public API to do that would be nice. It seems there are two implementations, one in Python, one in C. Maybe it's time you opened a bpo issue and started working on a PR? This just seems too reasonable to keep bugging python-ideas... On Thu, Oct 1, 2020 at 7:55 AM Ben Avrahami <avrahami.ben@gmail.com> wrote:
In theory, all that's necessary is to just run `_py_abc`'s abstract method population again (`ABCMeta.__new__`, with slight adjustments):
def update_abstractmethods(cls): if not isinstance(cls, ABCMeta): return cls abstracts = set() # include only abstract methods that are not implemented for name in cls.__abstractmethods__: value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) # check for new abstract methods # note that for most cases this loop is useless and can be scrapped for a minimal solution for name, value in cls.__dict__.items(): if name not in abstracts \ and getattr(value, "__isabstractmethod__", False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) return cls
My preferred API will be to just add this function into `abc`, but the minimal solution is to just airdrop this behaviour into `dataclass` (and possibly `total_ordering`), and let 3rd parties implement this logic themselves.
On Thu, Oct 1, 2020 at 5:08 PM Guido van Rossum <guido@python.org> wrote:
That might work (though not for older Python versions). What kind of API were you thinking of for adding and deleting abstract methods?
On Thu, Oct 1, 2020 at 01:07 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I suppose it's better than nothing, since right now libraries are (rightfully) afraid to modify it or rely on its behaviour. Although I expect this will lead to a lot of repeated code, that looks a lot like the `__abstractmethods__` population in `_py_abc`. I think it's preferable to ease abstract method re-population for 3rd (and 1st) party libraries, but the dev team probably knows better. If this is an accepted solution, I'd be happy to write a BPO and PR.
On Wed, Sep 30, 2020 at 5:30 PM Guido van Rossum <guido@python.org> wrote:
Would it help if ‘__abstractmethods__’ was documented, overruling whatever the PEP says?
On Wed, Sep 30, 2020 at 00:01 Ben Avrahami <avrahami.ben@gmail.com> wrote:
I encountered this problem when I needed to implement a class that defined all 4 of the comparison operators, once with `dataclass` (for one implementation) and once with `total_order` (for another).Also, 3rd party libs are expected to fall down this rabbit hole, and unless they're expected to modify `__abstractmethods__` and perform abstract logic on their own- I don't see why the standard library should keep such computations unavailable.
On Tue, Sep 29, 2020 at 6:41 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/29/2020 10:49 AM, Ben Avrahami
wrote:
On Tue, Sep 29, 2020 at 4:53
PM Bar Harel <bzvi7919@gmail.com> wrote:
> > I'd like to bring another insight to the > > table: According to the pep, "Dynamically > > adding abstract methods to a class, or attempting to > > modify the abstraction status of a method or class once > > it is created, are not supported." > > > > > > The notice exists both in the pep and at the abc module's > > docs, and is exactly what this idea is all about. > > > > > > > My question is why? Was there an objection for > > implementing such a thing, or was it complex enough to > > postpone for the time being? > > > > >
I think this is fine. The "abstract-ness" of a class is not
supposed to change throughout its lifetime. The problem
arises when decorators change the class before it's ever
used, as that should still be considered its
"initialization" as far as the user is concerned, so
its "abstract-ness" should change accordingly.
I agree with Guido in that the cleanest solution is to
change the decorators. Perhaps a module function to
recalculate a class's __abstractmethods __, that mixin
decorators can call prior to returning the class. I can't
really think of standard library class decorators that would
use this global function other than `dataclass` and
`total_ordering`, but in this way, 3rd party decorators can
easily support implementing abstract functions.
The one downside to this solution is that it can easily
be abused by calling it in the middle of a class's lifetime.
I think I'd like to see dataclasses handle this directly, so as
to keep it easy to use. But I can be argued with: it's not like a
beginner would stumble over this issue.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EQQVNE...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ASPJKU...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
--
--Guido (mobile)
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
On Thu, Sep 24, 2020 at 6:59 PM Guido van Rossum <guido@python.org> wrote:
Interesting problem. Since the solution is just updating `__abstractmethods__`, this could be done in the `@dataclass` decorator, no new ABC needed.
This issue also pertains to total_ordering (and perhaps other std library class-decorators I haven't thought of), not to mention 3rd party decorators that could have this problem (attrs, for example, does not cover this case). Updating __abstractmethods__ inside each of these decorators would solve the problem, but does seem like a pain for 3rd-party. Maybe a meta-decorator could be added to make this easier? @update_abstractmethods def dataclass(...): ...
participants (5)
-
Bar Harel
-
Ben Avrahami
-
Eric V. Smith
-
Guido van Rossum
-
William Pickard