- edited description
Hybrid decorators' "mutator decorators" actually mutate state, preventing specialising in a subclass
As it stands, hybrid_method
and hybrid_property
both mutate their state with the modifier decorators (setter
, deleter
, expression
, comparator
) in the following manner:
class hybrid_property:
...
def setter(self, fset):
self.fset = fset
return self
But this prevents me from specialising the property in a subclass:
class Foo(Model):
first_name = Column(String)
last_name = Column(String)
@hybrid_property
def name(self):
return self.first_name + ' ' + self.last_name
@name.setter
def name(self, value):
self.first_name, self.last_name = value.split(' ', maxsplit=1)
class Bar(Foo):
@Foo.name.setter
def name(self, value):
if run_name_check(value) > 0: # Error code received
raise InvalidName
super().name.fset(value)
Since that would result into modifying the original Foo.name
:
>>> f = Foo(first_name='Markus', last_name='Meskanen')
>>> f.name = 'Markus^% Meskanen'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.InvalidName
The correct way to implement these mutator decorators is to have them return a copy instance instead:
class hybrid_property:
...
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.expr)
I would PR such fix myself, but I'm in a hurry and couldn't quicky figure out how the expressions and comparators work exactly, and wasn't sure what is the intended behavior after this suggested fix would be applied.
Comments (7)
-
reporter -
reporter For what it's worth, this is how Python's built-in
property
works too; it always returns a new instance:>>> class Foo: ... ... @property ... def x(self): ... pass ... ... _x = x # Copy a reference of the current property ... ... @x.setter ... def x(self, value): ... pass ... ... print(_x is x) ... print(_x, x) ... False <property object at 0x7fa243d599a8> <property object at 0x7fa243d59958>
-
repo owner - changed milestone to 1.2
- marked as enhancement
I'm putting this as "enhancement" since I hadn't realized this use case originally, else I would have made it copy from the start. This would be bundled with
#3911and noted in changes to hybrids in the migration document. targeting for 1.2 but this may move out unless I can get contributions including tests. -
reporter Yeah I guess it's a bit of both, but fair enough.
I've never contributed to SQLAlchemy before but would love to help, any ETA when 1.2 will be out? Got a bit busy schedule and will have to investigate the project further before contributing too much.
-
repo owner 1.2 is a "big" release and I haven't really started organizing all the patches, it's at best for spring 2017 and probably later. for now I'd consider vendoring your own hybrid decorator as this is a simple extension.
-
-
assigned issue to
-
assigned issue to
-
repo owner - changed status to resolved
Allow reuse of hybrid_property across subclasses
The :class:
sqlalchemy.ext.hybrid.hybrid_property
class now supports calling mutators like@setter
,@expression
etc. multiple times across subclasses, and now provides a@getter
mutator, so that a particular hybrid can be repurposed across subclasses or other classes. This now matches the behavior of@property
in standard Python.Co-authored-by: Mike Bayer mike_mp@zzzcomputing.com Fixes:
#3911Fixes:#3912Change-Id: Iff033d8ccaae20ded9289cbfa789c376759381f5→ <<cset caeb274e287f>>
- Log in to comment