Package Gnumed :: Package timelinelib :: Package general :: Module immutable |
[frames] | no frames]

# Source Code for Module Gnumed.timelinelib.general.immutable

      1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
      2  #
      3  # This file is part of Timeline.
      4  #
      5  # Timeline is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Timeline is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>.
     17
     18
     19 -class ImmutableDict(tuple):
     20
     21 -    def __new__(cls, *args, **kwargs):
     22          if (len(args) == 1 and
     23              len(kwargs) == 0 and
     24              isinstance(args[0], _AlreadyCopiedDict)):
     25              d = args[0].value
     26          else:
     27              d = {}
     28              for arg in args:
     29                  d.update(arg)
     30              for key, value in kwargs.iteritems():
     31                  d[key] = value
     32          return tuple.__new__(cls, (d,))
     33
     34      @property
     35 -    def _internal(self):
     36          return tuple.__getitem__(self, 0)
     37
     38 -    def update(self, *args, **kwargs):
     39          return self.__class__(self._internal, *args, **kwargs)
     40
     41 -    def remove(self, key):
     42          new = {}
     43          new.update(self._internal)
     44          del new[key]
     45          return self.__class__(_AlreadyCopiedDict(new))
     46
     47 -    def map(self, fn):
     48          return self.__class__(_AlreadyCopiedDict({
     49              key: fn(value)
     50              for key, value
     51              in self._internal.iteritems()
     52          }))
     53
     54 -    def get(self, name, default=None):
     55          return self._internal.get(name, default)
     56
     57 -    def __len__(self):
     58          return len(self._internal)
     59
     60 -    def __contains__(self, item):
     61          return item in self._internal
     62
     63 -    def __getitem__(self, name):
     64          return self._internal[name]
     65
     66 -    def __iter__(self):
     67          return self._internal.iteritems()
     68
     69 -    def __eq__(self, other):
     70          return (
     71              isinstance(other, self.__class__) and
     72              self._internal == other._internal
     73          )
     74
     75 -    def __ne__(self, other):
     76          return not self == other
     77
     78 -    def __repr__(self):
     79          items = []
     80          items.append(self.__class__.__name__)
     81          items.append("({\n")
     82          for key, value in self:
     83              items.append("  ")
     84              items.append(repr(key))
     85              items.append(": ")
     86              for index, line in enumerate(repr(value).split("\n")):
     87                  if index > 0:
     88                      items.append("\n  ")
     89                  items.append(line)
     90              items.append(",\n")
     91          items.append("})")
     92          return "".join(items)
     93
     94
     95 -class ImmutableRecordMeta(type):
     96
     97 -    def __new__(cls, name, bases, attrs):
     98          def create_property(name):
     99              return property(
    100                  lambda self: self.get(name)
    101              )
    102          base_attributes = []
    103          for base in bases:
    104              base_attributes.extend(dir(base))
    105          fields = {}
    106          for key, value in list(attrs.iteritems()):
    107              if isinstance(value, Field):
    108                  if key in base_attributes:
    109                      raise ValueError(
    110                          "{0!r} is a reserved field name".format(key)
    111                      )
    112                  fields[key] = value
    113                  attrs[key] = create_property(key)
    114          attrs["_immutable_record_fields"] = fields
    115          return super(ImmutableRecordMeta, cls).__new__(cls, name, bases, attrs)
    116
    117
    118 -class Field(object):
    119
    120 -    def __init__(self, default=None):
    121          self.default = default
    122
    123
    124 -class ImmutableRecord(ImmutableDict):
    125
    126      __metaclass__ = ImmutableRecordMeta
    127
    128 -    def __new__(cls, *args, **kwargs):
    129          defaults = {
    130              key: value.default
    131              for key, value
    132              in cls._immutable_record_fields.iteritems()
    133          }
    134          d = ImmutableDict.__new__(cls, defaults, *args, **kwargs)
    135          for key, value in d:
    136              if key not in cls._immutable_record_fields:
    137                  raise ValueError("{0!r} is not a valid field of {1}".format(
    138                      key,
    139                      cls.__name__
    140                  ))
    141          return d
    142
    143
    144 -class _AlreadyCopiedDict(object):
    145
    146      """
    147      A special value that can be passed as the single value to the constructor
    148      of ImmutableDict to prevent unnecessary copying.
    149      """
    150
    151 -    def __init__(self, value):
    152          self.value = value
    153