-
-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #588 from jaraco/feature/jaraco.classes
Use jaraco.classes for properties
- Loading branch information
Showing
11 changed files
with
194 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
__all__ = ['properties'] | ||
|
||
|
||
try: | ||
from jaraco.compat import properties # pragma: no-cover | ||
except ImportError: | ||
from . import _properties_compat as properties # pragma: no-cover |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
# from jaraco.classes 3.2.2 | ||
|
||
|
||
class NonDataProperty: | ||
"""Much like the property builtin, but only implements __get__, | ||
making it a non-data property, and can be subsequently reset. | ||
See http://users.rcn.com/python/download/Descriptor.htm for more | ||
information. | ||
>>> class X(object): | ||
... @NonDataProperty | ||
... def foo(self): | ||
... return 3 | ||
>>> x = X() | ||
>>> x.foo | ||
3 | ||
>>> x.foo = 4 | ||
>>> x.foo | ||
4 | ||
""" | ||
|
||
def __init__(self, fget): | ||
assert fget is not None, "fget cannot be none" | ||
assert callable(fget), "fget must be callable" | ||
self.fget = fget | ||
|
||
def __get__(self, obj, objtype=None): | ||
if obj is None: | ||
return self | ||
return self.fget(obj) | ||
|
||
|
||
class classproperty: | ||
""" | ||
Like @property but applies at the class level. | ||
>>> class X(metaclass=classproperty.Meta): | ||
... val = None | ||
... @classproperty | ||
... def foo(cls): | ||
... return cls.val | ||
... @foo.setter | ||
... def foo(cls, val): | ||
... cls.val = val | ||
>>> X.foo | ||
>>> X.foo = 3 | ||
>>> X.foo | ||
3 | ||
>>> x = X() | ||
>>> x.foo | ||
3 | ||
>>> X.foo = 4 | ||
>>> x.foo | ||
4 | ||
Setting the property on an instance affects the class. | ||
>>> x.foo = 5 | ||
>>> x.foo | ||
5 | ||
>>> X.foo | ||
5 | ||
>>> vars(x) | ||
{} | ||
>>> X().foo | ||
5 | ||
Attempting to set an attribute where no setter was defined | ||
results in an AttributeError: | ||
>>> class GetOnly(metaclass=classproperty.Meta): | ||
... @classproperty | ||
... def foo(cls): | ||
... return 'bar' | ||
>>> GetOnly.foo = 3 | ||
Traceback (most recent call last): | ||
... | ||
AttributeError: can't set attribute | ||
It is also possible to wrap a classmethod or staticmethod in | ||
a classproperty. | ||
>>> class Static(metaclass=classproperty.Meta): | ||
... @classproperty | ||
... @classmethod | ||
... def foo(cls): | ||
... return 'foo' | ||
... @classproperty | ||
... @staticmethod | ||
... def bar(): | ||
... return 'bar' | ||
>>> Static.foo | ||
'foo' | ||
>>> Static.bar | ||
'bar' | ||
*Legacy* | ||
For compatibility, if the metaclass isn't specified, the | ||
legacy behavior will be invoked. | ||
>>> class X: | ||
... val = None | ||
... @classproperty | ||
... def foo(cls): | ||
... return cls.val | ||
... @foo.setter | ||
... def foo(cls, val): | ||
... cls.val = val | ||
>>> X.foo | ||
>>> X.foo = 3 | ||
>>> X.foo | ||
3 | ||
>>> x = X() | ||
>>> x.foo | ||
3 | ||
>>> X.foo = 4 | ||
>>> x.foo | ||
4 | ||
Note, because the metaclass was not specified, setting | ||
a value on an instance does not have the intended effect. | ||
>>> x.foo = 5 | ||
>>> x.foo | ||
5 | ||
>>> X.foo # should be 5 | ||
4 | ||
>>> vars(x) # should be empty | ||
{'foo': 5} | ||
>>> X().foo # should be 5 | ||
4 | ||
""" | ||
|
||
class Meta(type): | ||
def __setattr__(self, key, value): | ||
obj = self.__dict__.get(key, None) | ||
if type(obj) is classproperty: | ||
return obj.__set__(self, value) | ||
return super().__setattr__(key, value) | ||
|
||
def __init__(self, fget, fset=None): | ||
self.fget = self._ensure_method(fget) | ||
self.fset = fset | ||
fset and self.setter(fset) | ||
|
||
def __get__(self, instance, owner=None): | ||
return self.fget.__get__(None, owner)() | ||
|
||
def __set__(self, owner, value): | ||
if not self.fset: | ||
raise AttributeError("can't set attribute") | ||
if type(owner) is not classproperty.Meta: | ||
owner = type(owner) | ||
return self.fset.__get__(None, owner)(value) | ||
|
||
def setter(self, fset): | ||
self.fset = self._ensure_method(fset) | ||
return self | ||
|
||
@classmethod | ||
def _ensure_method(cls, fn): | ||
""" | ||
Ensure fn is a classmethod or staticmethod. | ||
""" | ||
needs_method = not isinstance(fn, (classmethod, staticmethod)) | ||
return classmethod(fn) if needs_method else fn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.