0001from ohm.validators import JSONConverter, LineConverter
0002
0003def simple_repr(func_name, *args, **kw):
0004    """
0005    Helper function for reprs like 'func_name(args, keywords)'
0006    """
0007    args = [repr(x) for x in args]
0008    args.extend(['%s=%r' % (name, value)
0009                 for name, value in sorted(kw.items())])
0010    return '%s(%s)' % (func_name, ', '.join(args))
0011
0012class cache(object):
0013
0014    """
0015    A caching descriptor wrapper.  Takes the attribute name and the
0016    wrapped descriptor as arguments.
0017
0018    To expire, use ``MyClass.cached_attr.expire(my_object)`` where
0019    ``my_object`` is an instance of ``MyClass``.
0020    """
0021
0022    def __init__(self, name, getter):
0023        self.name = name
0024        self._attr_name = '_cache_%s' % name
0025        self.getter = getter
0026
0027    def __get__(self, obj, type=None):
0028        if obj is None:
0029            return self
0030        try:
0031            return getattr(obj, self._attr_name)
0032        except AttributeError:
0033            pass
0034        value = self.getter.__get__(obj, type)
0035        setattr(obj, self._attr_name, value)
0036        return value
0037
0038    def __set__(self, obj, value):
0039        self.getter.__set__(obj, value)
0040        setattr(obj, self._attr_name, value)
0041
0042    def __delete__(self, obj):
0043        self.getter.__delete__(obj)
0044        self.expire(obj)
0045
0046    def expire(self, obj):
0047        try:
0048            delattr(obj, self._attr_name)
0049        except AttributeError:
0050            pass
0051
0052    def __repr__(self):
0053        return simple_repr('cache', self.name, self.getter)
0054
0055class converter(object):
0056
0057    """
0058    Applies a FormEncode validator/converter to a descriptor.
0059    Supports both a class-level ``default_validator``, and custom
0060    validators.
0061
0062    Subclasses (like ``json_converter``) might set
0063    ``default_validator``.
0064    """
0065
0066    default_validator = None
0067
0068    def __init__(self, getter=None, validator=None):
0069        self.getter = getter
0070        # Extra validation, after native validation
0071        self.validator = validator
0072
0073    def __get__(self, obj, type=None):
0074        if obj is None:
0075            return self
0076        value = self.getter.__get__(obj, type)
0077        if self.validator:
0078            value = self.validator.to_python(value)
0079        if self.default_validator:
0080            value = self.default_validator.to_python(value)
0081        return value
0082
0083    def __set__(self, obj, value):
0084        if self.default_validator:
0085            value = self.default_validator.from_python(value)
0086        if self.validator:
0087            value = self.validator.from_python(value)
0088        self.getter.__set__(obj, value)
0089
0090    def __delete__(self, obj):
0091        self.getter.__delete__(obj)
0092
0093    def __repr__(self):
0094        return simple_repr(self.__class__.__name__,
0095                           self.getter)
0096
0097    def __getattr__(self, attr):
0098        # All other attributes are delegated to the getter
0099        return getattr(self.getter, attr)
0100
0101class watcher(object):
0102    """
0103    Calls a callback function with (obj, old_value, new_value) everytime
0104    an attribute is set.
0105
0106    You can listen before or after the value is actually set, with
0107    before_watcher and after_watcher.  delete_watcher (if given) is
0108    called with (self, old_value) everytime an attribute is deleted,
0109    right before the delete.
0110    """
0111
0112    def __init__(self, getter, before_watcher=None,
0113                 after_watcher=None, delete_watcher=None):
0114        self.getter = getter
0115        self.before_watcher = before_watcher
0116        self.after_watcher = after_watcher
0117        self.delete_watcher = delete_watcher
0118
0119    def __get__(self, obj, type=None):
0120        if obj is None:
0121            return self
0122        value = self.getter.__get__(obj, type)
0123        return value
0124
0125    def __set__(self, obj, value):
0126        old_value = self.getter.__get__(obj, None)
0127        if self.before_watcher is not None:
0128            self.before_watcher(obj, old_value, value)
0129        self.getter.__set__(obj, value)
0130        if self.after_watcher is not None:
0131            self.after_watcher(obj, old_value, value)
0132
0133    def __delete__(self, obj):
0134        if self.delete_watcher is not None:
0135            old_value = self.getter.__get__(obj, None)
0136            self.delete_watcher(obj, old_value)
0137        self.getter.__delete__(obj)
0138
0139    def __repr__(self):
0140        kw = {}
0141        if self.before_watcher:
0142            kw['before_watcher'] = self.before_watcher
0143        if self.after_watcher:
0144            kw['after_watcher'] = self.after_watcher
0145        if self.delete_watcher:
0146            kw['delete_watcher'] = self.delete_watcher
0147        return simple_repr(self.__class__.__name__,
0148                           self.getter, **kw)
0149
0150
0151class json_converter(converter):
0152    """
0153    Wraps a descriptor and does JSON decoding/encoding.
0154
0155    An underlying string shows up as the JSON it decodes to.
0156    """
0157
0158    default_validator = JSONConverter()
0159
0160class line_converter(converter):
0161    """
0162    Wraps a descriptor, and splits/joins text lines.
0163
0164    An underlying string shows up as a list of strings split by line.
0165    """
0166    default_validator = LineConverter()