From 53ae9f332a7548e94927684ebbe1868194c15ba1 Mon Sep 17 00:00:00 2001 From: helmutm Date: Thu, 1 Mar 2007 08:46:45 +0000 Subject: [PATCH] included rwproperty stuff in util.property git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1602 fd906abe-77d9-0310-91a1-e0d9ade77398 --- util/property.py | 79 +++++++++++++++++++++++++++++++++++- util/property.txt | 101 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 3 deletions(-) diff --git a/util/property.py b/util/property.py index 0881d4a..a78f834 100644 --- a/util/property.py +++ b/util/property.py @@ -17,9 +17,11 @@ # """ -Advanced properties, esp the lazy one... +Advanced properties, i.e. the lazy one and Philipp von Weitershausen's +read & write properties. -Based on zope.cachedescriptors.property.Lazy +Based on zope.cachedescriptors.property.Lazy and including code from +http://cheeseshop.python.org/pypi/rwproperty. $Id$ """ @@ -39,3 +41,76 @@ class lzprop(object): value = self.func(inst) inst.__dict__[self.func.__name__] = value return value + + +# Read & write properties +# +# Copyright (c) 2006 by Philipp "philiKON" von Weitershausen +# philikon@philikon.de +# +# Freely distributable under the terms of the Zope Public License, v2.1. +# +# See rwproperty.txt for detailed explanations +# +import sys + +__all__ = ['getproperty', 'setproperty', 'delproperty'] + +class rwproperty(object): + + def __new__(cls, func): + name = func.__name__ + + # ugly, but common hack + frame = sys._getframe(1) + locals = frame.f_locals + + if name not in locals: + return cls.createProperty(func) + + oldprop = locals[name] + if isinstance(oldprop, property): + return cls.enhanceProperty(oldprop, func) + + raise TypeError("read & write properties cannot be mixed with " + "other attributes except regular property objects.") + + # this might not be particularly elegant, but it's easy on the eyes + + @staticmethod + def createProperty(func): + raise NotImplementedError + + @staticmethod + def enhanceProperty(oldprop, func): + raise NotImplementedError + +class getproperty(rwproperty): + + @staticmethod + def createProperty(func): + return property(func) + + @staticmethod + def enhanceProperty(oldprop, func): + return property(func, oldprop.fset, oldprop.fdel) + +class setproperty(rwproperty): + + @staticmethod + def createProperty(func): + return property(None, func) + + @staticmethod + def enhanceProperty(oldprop, func): + return property(oldprop.fget, func, oldprop.fdel) + +class delproperty(rwproperty): + + @staticmethod + def createProperty(func): + return property(None, None, func) + + @staticmethod + def enhanceProperty(oldprop, func): + return property(oldprop.fget, oldprop.fset, func) diff --git a/util/property.txt b/util/property.txt index 62629f0..039d34b 100644 --- a/util/property.txt +++ b/util/property.txt @@ -7,7 +7,7 @@ $Id$ lzprop ====== -The `lzprop` decorator allows the declaration of lazy properties - attributes +The ``@lzprop`` decorator allows the declaration of lazy properties - attributes that are calculated only on first access, and then just once. This is extremely useful when working with objects of a limited lifetime (e.g. adapters) that provide results from expensive calculations. @@ -52,3 +52,102 @@ already during compilation or object creation): >>> demo2.value 105 + +rwproperty +========== + +:Author: Philipp von Weitershausen +:Email: philikon@philikon.de +:License: Zope Public License, v2.1 + +Goal +---- + +There should be a way to declare a read & write property and still use +the compact and easy decorator spelling. The read & write properties +should be as easy to use as the read-only property. We explicitly +don't want that immediately called function that really just helps us +name the attribute and create a local scope for the getter and setter. + + +Read & write property +--------------------- + +Read & write properties work like regular properties. You simply +define a method and then apply a decorator, except that you now don't +use ``@property`` but ``@getproperty`` to mark the getter and +``@setproperty`` to mark the setter: + + >>> from cybertools.util.property import getproperty, setproperty, delproperty + + >>> class JamesBrown(object): + ... @getproperty + ... def feel(self): + ... return self._feel + ... @setproperty + ... def feel(self, feel): + ... self._feel = feel + + >>> i = JamesBrown() + >>> i.feel + Traceback (most recent call last): + ... + AttributeError: 'JamesBrown' object has no attribute '_feel' + + >>> i.feel = "good" + >>> i.feel + 'good' + +The order in which getters and setters are declared doesn't matter: + + >>> class JamesBrown(object): + ... @setproperty + ... def feel(self, feel): + ... self._feel = feel + ... @getproperty + ... def feel(self): + ... return self._feel + + >>> i = JamesBrown() + >>> i.feel = "good" + >>> i.feel + 'good' + +Of course, deleters are also possible: + + >>> class JamesBrown(object): + ... @setproperty + ... def feel(self, feel): + ... self._feel = feel + ... @getproperty + ... def feel(self): + ... return self._feel + ... @delproperty + ... def feel(self): + ... del self._feel + + >>> i = JamesBrown() + >>> i.feel = "good" + >>> del i.feel + >>> i.feel + Traceback (most recent call last): + ... + AttributeError: 'JamesBrown' object has no attribute '_feel' + + +Edge cases +---------- + +There might be a case where you're using a flavour of read & write +properties and already have a non-property attribute of the same name +defined: + + >>> class JamesBrown(object): + ... feel = "good" + ... @getproperty + ... def feel(self): + ... return "so good" + ... + Traceback (most recent call last): + ... + TypeError: read & write properties cannot be mixed with other attributes except regular property objects.