pytilities.delegation.profile

Package pytilities :: Package delegation :: Module profile |
[hide private]
[frames] | no frames]

# Source Code for Module pytilities.delegation.profile

      1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net>
      2  #
      3  # This file is part of pytilities.
      4  #
      5  # pytilities 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  # pytilities 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 pytilities.  If not, see <http://www.gnu.org/licenses/>.
     17  #
     18
     19  __docformat__ = 'reStructuredText'
     20
     21  import copy
     22
     23 -class Profile(object):
     24
     25      """
     26      States what attributes to delegate, and to which target attributes.
     27
     28      Instance methods:
     29
     30          - `has_readable`: Check for read-access mapping of attribute
     31          - `has_writable`: Check for read-access mapping of attribute
     32          - `has_deletable`: Check for read-access mapping of attribute
     33          - `get_readable`: Get read-access mapping for attribute
     34          - `get_writable`: Get read-access mapping for attribute
     35          - `get_deletable`: Get read-access mapping for attribute
     36          - `remove_mappings`: Remove attribute mappings
     37          - `add_mappings`: Add attribute mappings
     38
     39      Operators:
     40
     41          self |= b::Profile
     42              union of this profile and the other
     43
     44          self | b::Profile
     45              union of this profile and the other
     46
     47          self &= b::Profile
     48              intersection of this profile and the other
     49
     50          self & b::Profile
     51              intersection of this profile and the other
     52
     53          self - b::Profile
     54              difference of this profile and the other
     55
     56          self -= b::Profile
     57              difference of this profile and the other
     58
     59          self ^ b::Profile
     60              symetric difference of this profile and the other
     61
     62          self ^= b::Profile
     63              symetric difference of this profile and the other
     64      """
     65
     66 -    def __init__(self):
     67          self.__readables = {}
     68          self.__writables = {}
     69          self.__deletables = {}
     70
     71 -    def has_readable(self, attribute):
     72          """
     73          Check for read-access mapping of attribute
     74
     75          Returns True if the mapping exists
     76          """
     77          return attribute in self.__readables
     78
     79 -    def has_writable(self, attribute):
     80          """
     81          Check for write-access mapping of attribute
     82
     83          Returns True if the mapping exists
     84          """
     85          return attribute in self.__writables
     86
     87 -    def has_deletable(self, attribute):
     88          """
     89          Check for delete-access mapping of attribute
     90
     91          Returns True if the mapping exists
     92          """
     93          return attribute in self.__deletables
     94
     95 -    def get_readable(self, attribute):
     96          """
     97          Get the mapped value of a readable attribute
     98
     99          Returns ::string
    100          """
    101          return self.__readables[attribute]
    102
    103 -    def get_writable(self, attribute):
    104          """
    105          Get the mapped value of a writable attribute
    106
    107          Returns ::string
    108          """
    109          return self.__writables[attribute]
    110
    111 -    def get_deletable(self, attribute):
    112          """
    113          Get the mapped value of a deletable attribute
    114
    115          Returns ::string
    116          """
    117          return self.__deletables[attribute]
    118
    119 -    def __are_conflict_free(self, other):
    120          '''
    121          Returns True if mappings of self don't conflict with those of other
    122          '''
    123          # check for conflicts: intersect keys, then see whether intersections
    124          # map to the same values
    125          # If you get an assertion failure about this, then you should check
    126          # your bases to see if any of them map an attribute differently
    127          # e.g. with profiles a, b and attribute x: a.x should map to the same
    128          # value as b.x
    129          return reduce(lambda x, y: x and y,
    130                        (reduce(lambda x,y: x and y,
    131                                (a[key] == b[key]
    132                                for key
    133                                in set(a.iterkeys()) & set(b.iterkeys())),
    134                                True)
    135                        for a, b
    136                        in zip((self.__readables, self.__writables,
    137                               self.__deletables),
    138                              (other.__readables, other.__writables,
    139                               other.__deletables))),
    140                        True)
    141
    142      # the | operator
    143 -    def __or__(self, other):
    144          '''See __ior__'''
    145          p = self.copy()
    146          p |= other
    147          return p
    148
    149 -    def __ior__(self, other):
    150          """
    151          union of this profile and the other
    152
    153          Parameters:
    154
    155              - `other` :: Profile
    156                  the other profile
    157
    158          Preconditions:
    159              1. The mappings of the two profiles musn't conflict. Two profiles
    160              conflict when they have at least one mapping with the same source
    161              name, but a different target name.
    162          """
    163          assert self.__are_conflict_free(other), \
    164              "Profiles have conflicting mappings, can't take union"
    165
    166          # no conflicts, go ahead and update
    167          self.__readables.update(other.__readables)
    168          self.__writables.update(other.__writables)
    169          self.__deletables.update(other.__deletables)
    170          return self
    171
    172 -    def __and__(self, other):
    173          p = self.copy()
    174          p &= other
    175          return p
    176
    177 -    def __iand__(self, other):
    178          """
    179          intersection of this profile and the other
    180
    181          Parameters:
    182
    183              - `other` :: Profile
    184                  the other profile
    185
    186          Preconditions:
    187              1. The mappings of the two profiles musn't conflict. Two profiles
    188              conflict when they have at least one mapping with the same source
    189              name, but a different target name.
    190          """
    191
    192          assert self.__are_conflict_free(other), \
    193              "Profiles have conflicting mappings, can't take union"
    194
    195          # no conflicts, go ahead and update
    196          for a, b in zip(
    197              (self.__readables, self.__writables, self.__deletables),
    198              (other.__readables, other.__writables, other.__deletables)):
    199              for key in a.keys():
    200                  if key not in b:
    201                      del a[key]
    202
    203          return self
    204
    205
    206 -    def __sub__(self, other):
    207          p = self.copy()
    208          p -= other
    209          return p
    210
    211 -    def __isub__(self, other):
    212          """
    213          difference of this profile and the other
    214
    215          Parameters:
    216
    217              - `other` :: Profile
    218                  the other profile
    219          """
    220
    221          for a, b in zip(
    222              (self.__readables, self.__writables, self.__deletables),
    223              (other.__readables, other.__writables, other.__deletables)):
    224              for key in a.keys():
    225                  if key in b:
    226                      del a[key]
    227
    228          return self
    229
    230
    231 -    def __xor__(self, other):
    232          p = self.copy()
    233          p ^= other
    234          return p
    235
    236 -    def __ixor__(self, other):
    237          """
    238          symetric difference of this profile and the other
    239
    240          Parameters:
    241
    242              - `other` :: Profile
    243                  the other profile
    244          """
    245
    246          # get the intersection of keys
    247          inter_readables = set(self.__readables.iterkeys()) \
    248                           & set(other.__readables.iterkeys())
    249          inter_writables = set(self.__writables.iterkeys()) \
    250                           & set(other.__writables.iterkeys())
    251          inter_deletables = set(self.__deletables.iterkeys()) \
    252                           & set(other.__deletables.iterkeys())
    253
    254          # or the two together
    255          self.__readables.update(other.__readables)
    256          self.__writables.update(other.__writables)
    257          self.__deletables.update(other.__deletables)
    258
    259          # remove the intersection
    260          for a, b in zip(
    261              (self.__readables, self.__writables, self.__deletables),
    262              (inter_readables, inter_writables, inter_deletables)):
    263              for key in a.keys():
    264                  if key in b:
    265                      del a[key]
    266
    267          return self
    268
    269 -    def remove_mappings(self, modifiers, *names):
    270          """
    271          Remove attribute mappings
    272
    273          Parameters:
    274
    275              `modifiers` :: string
    276                  the types of delegation access to the target to remove. Valid
    277                  modifiers are combinations of:
    278
    279                      - `r`: read
    280                      - `w`: write
    281                      - `d`: delete
    282
    283              `names` :: (source_name::string...)
    284                  names of source_name parts of, the mappings to remove
    285          """
    286
    287          for name in names:
    288              assert isinstance(name, basestring), \
    289                      "Invalid arguments: names should be list of strings: %s" \
    290                      % (names)
    291
    292          for name in names:
    293              if "r" in modifiers:
    294                  self.__readables.pop(name)
    295
    296              if "w" in modifiers:
    297                  self.__writables.pop(name)
    298
    299              if "d" in modifiers:
    300                  self.__deletables.pop(name)
    301
    302
    303 -    def add_mappings(self, modifiers, *names, **mapped_names):
    304          """
    305          Map source to target attribute names, for delegation.
    306
    307          Parameters:
    308
    309              `modifiers` :: string
    310                  the types of delegation access to the target. Valid modifiers
    311                  are combinations of:
    312
    313                      - `r`: read
    314                      - `w`: write
    315                      - `d`: delete
    316
    317              `names` :: (source_name::string...)
    318                  names of the attribute names to delegate. The target attribute
    319                  name is the same as `source_name`. This is the same as adding a
    320                  {source_name : source_name} mapping, using the mapped_names
    321                  argument.
    322
    323              `mapped_names` :: {source_name::string : target_name::string ...}
    324                  names of source and target attributes to map and delegate.
    325
    326                  `target_name`s will be mangled if they have a __ prefix.
    327                  Mangling will occur every time it is delegated to, so you can
    328                  change the target object any time.
    329          """
    330          for name in names:
    331              assert isinstance(name, basestring), (
    332                  "Invalid arguments: names should be list of strings. Got: %s" %
    333                  names)
    334
    335          for key, name in mapped_names.iteritems():
    336              assert isinstance(key, basestring) and \
    337                      isinstance(name, basestring), \
    338                      "Invalid arguments: mapped_names should be dict of " + \
    339                      "string:string. Got: %s" % mapped_names
    340
    341          if "r" in modifiers:
    342              self.__readables.update(zip(names, names), **mapped_names)
    343
    344          if "w" in modifiers:
    345              self.__writables.update(zip(names, names), **mapped_names)
    346
    347          if "d" in modifiers:
    348              self.__deletables.update(zip(names, names), **mapped_names)
    349
    350 -    def copy(self):
    351          return copy.deepcopy(self)
    352