Package mvpa :: Package support :: Module _copy

[hide private]
[frames] | no frames]

# Source Code for Module mvpa.support._copy

      1  """Generic (shallow and deep) copying operations.
      2
      3  Interface summary:
      4
      5          import copy
      6
      7          x = copy.copy(y)        # make a shallow copy of y
      8          x = copy.deepcopy(y)    # make a deep copy of y
      9
     10  For module specific errors, copy.Error is raised.
     11
     12  The difference between shallow and deep copying is only relevant for
     13  compound objects (objects that contain other objects, like lists or
     14  class instances).
     15
     16  - A shallow copy constructs a new compound object and then (to the
     17    extent possible) inserts *the same objects* into it that the
     18    original contains.
     19
     20  - A deep copy constructs a new compound object and then, recursively,
     21    inserts *copies* into it of the objects found in the original.
     22
     23  Two problems often exist with deep copy operations that don't exist
     24  with shallow copy operations:
     25
     26   a) recursive objects (compound objects that, directly or indirectly,
     27      contain a reference to themselves) may cause a recursive loop
     28
     29   b) because deep copy copies *everything* it may copy too much, e.g.
     30      administrative data structures that should be shared even between
     31      copies
     32
     33  Python's deep copy operation avoids these problems by:
     34
     35   a) keeping a table of objects already copied during the current
     36      copying pass
     37
     38   b) letting user-defined classes override the copying operation or the
     39      set of components copied
     40
     41  This version does not copy types like module, class, function, method,
     42  nor stack trace, stack frame, nor file, socket, window, nor array, nor
     43  any similar types.
     44
     45  Classes can use the same interfaces to control copying that they use
     46  to control pickling: they can define methods called __getinitargs__(),
     47  __getstate__() and __setstate__().  See the documentation for module
     48  "pickle" for information on these methods.
     49  """
     50
     51  import types
     52  from copy_reg import dispatch_table
     53
     54 -class Error(Exception):
     55      pass
     56  error = Error   # backward compatibility
     57
     58  try:
     59      from org.python.core import PyStringMap
     60  except ImportError:
     61      PyStringMap = None
     62
     63  __all__ = ["Error", "copy", "deepcopy"]
     64
     65 -def copy(x):
     66      """Shallow copy operation on arbitrary Python objects.
     67
     68      See the module's __doc__ string for more info.
     69      """
     70
     71      cls = type(x)
     72
     73      copier = _copy_dispatch.get(cls)
     74      if copier:
     75          return copier(x)
     76
     77      copier = getattr(cls, "__copy__", None)
     78      if copier:
     79          return copier(x)
     80
     81      reductor = dispatch_table.get(cls)
     82      if reductor:
     83          rv = reductor(x)
     84      else:
     85          reductor = getattr(x, "__reduce_ex__", None)
     86          if reductor:
     87              rv = reductor(2)
     88          else:
     89              reductor = getattr(x, "__reduce__", None)
     90              if reductor:
     91                  rv = reductor()
     92              else:
     93                  raise Error("un(shallow)copyable object of type %s" % cls)
     94
     95      return _reconstruct(x, rv, 0)
     96
     97
     98  _copy_dispatch = d = {}
     99
    100 -def _copy_immutable(x):
    101      return x
    102  for t in (type(None), int, long, float, bool, str, tuple,
    103            frozenset, type, xrange, types.ClassType,
    104            types.BuiltinFunctionType, type(Ellipsis),
    105            types.FunctionType):
    106      d[t] = _copy_immutable
    107  for name in ("ComplexType", "UnicodeType", "CodeType"):
    108      t = getattr(types, name, None)
    109      if t is not None:
    110          d[t] = _copy_immutable
    111
    112 -def _copy_with_constructor(x):
    113      return type(x)(x)
    114  for t in (list, dict, set):
    115      d[t] = _copy_with_constructor
    116
    117 -def _copy_with_copy_method(x):
    118      return x.copy()
    119  if PyStringMap is not None:
    120      d[PyStringMap] = _copy_with_copy_method
    121
    122 -def _copy_inst(x):
    123      if hasattr(x, '__copy__'):
    124          return x.__copy__()
    125      if hasattr(x, '__getinitargs__'):
    126          args = x.__getinitargs__()
    127          y = x.__class__(*args)
    128      else:
    129          y = _EmptyClass()
    130          y.__class__ = x.__class__
    131      if hasattr(x, '__getstate__'):
    132          state = x.__getstate__()
    133      else:
    134          state = x.__dict__
    135      if hasattr(y, '__setstate__'):
    136          y.__setstate__(state)
    137      else:
    138          y.__dict__.update(state)
    139      return y
    140  d[types.InstanceType] = _copy_inst
    141
    142  del d
    143
    144 -def deepcopy(x, memo=None, _nil=[]):
    145      """Deep copy operation on arbitrary Python objects.
    146
    147      See the module's __doc__ string for more info.
    148      """
    149
    150      if memo is None:
    151          memo = {}
    152
    153      d = id(x)
    154      y = memo.get(d, _nil)
    155      if y is not _nil:
    156          return y
    157
    158      cls = type(x)
    159
    160      copier = _deepcopy_dispatch.get(cls)
    161      if copier:
    162          y = copier(x, memo)
    163      else:
    164          try:
    165              issc = issubclass(cls, type)
    166          except TypeError: # cls is not a class (old Boost; see SF #502085)
    167              issc = 0
    168          if issc:
    169              y = _deepcopy_atomic(x, memo)
    170          else:
    171              copier = getattr(x, "__deepcopy__", None)
    172              if copier:
    173                  y = copier(memo)
    174              else:
    175                  reductor = dispatch_table.get(cls)
    176                  if reductor:
    177                      rv = reductor(x)
    178                  else:
    179                      reductor = getattr(x, "__reduce_ex__", None)
    180                      if reductor:
    181                          rv = reductor(2)
    182                      else:
    183                          reductor = getattr(x, "__reduce__", None)
    184                          if reductor:
    185                              rv = reductor()
    186                          else:
    187                              raise Error(
    188                                  "un(deep)copyable object of type %s" % cls)
    189                  y = _reconstruct(x, rv, 1, memo)
    190
    191      memo[d] = y
    192      _keep_alive(x, memo) # Make sure x lives at least as long as d
    193      return y
    194
    195  _deepcopy_dispatch = d = {}
    196
    197 -def _deepcopy_atomic(x, memo):
    198      return x
    199  d[type(None)] = _deepcopy_atomic
    200  d[type(Ellipsis)] = _deepcopy_atomic
    201  d[int] = _deepcopy_atomic
    202  d[long] = _deepcopy_atomic
    203  d[float] = _deepcopy_atomic
    204  d[bool] = _deepcopy_atomic
    205  try:
    206      d[complex] = _deepcopy_atomic
    207  except NameError:
    208      pass
    209  d[str] = _deepcopy_atomic
    210  try:
    211      d[unicode] = _deepcopy_atomic
    212  except NameError:
    213      pass
    214  try:
    215      d[types.CodeType] = _deepcopy_atomic
    216  except AttributeError:
    217      pass
    218  d[type] = _deepcopy_atomic
    219  d[xrange] = _deepcopy_atomic
    220  d[types.ClassType] = _deepcopy_atomic
    221  d[types.BuiltinFunctionType] = _deepcopy_atomic
    222  d[types.FunctionType] = _deepcopy_atomic
    223
    224 -def _deepcopy_list(x, memo):
    225      y = []
    226      memo[id(x)] = y
    227      for a in x:
    228          y.append(deepcopy(a, memo))
    229      return y
    230  d[list] = _deepcopy_list
    231
    232 -def _deepcopy_tuple(x, memo):
    233      y = []
    234      for a in x:
    235          y.append(deepcopy(a, memo))
    236      d = id(x)
    237      try:
    238          return memo[d]
    239      except KeyError:
    240          pass
    241      for i in range(len(x)):
    242          if x[i] is not y[i]:
    243              y = tuple(y)
    244              break
    245      else:
    246          y = x
    247      memo[d] = y
    248      return y
    249  d[tuple] = _deepcopy_tuple
    250
    251 -def _deepcopy_dict(x, memo):
    252      y = {}
    253      memo[id(x)] = y
    254      for key, value in x.iteritems():
    255          y[deepcopy(key, memo)] = deepcopy(value, memo)
    256      return y
    257  d[dict] = _deepcopy_dict
    258  if PyStringMap is not None:
    259      d[PyStringMap] = _deepcopy_dict
    260
    261 -def _keep_alive(x, memo):
    262      """Keeps a reference to the object x in the memo.
    263
    264      Because we remember objects by their id, we have
    265      to assure that possibly temporary objects are kept
    266      alive by referencing them.
    267      We store a reference at the id of the memo, which should
    268      normally not be used unless someone tries to deepcopy
    269      the memo itself...
    270      """
    271      try:
    272          memo[id(memo)].append(x)
    273      except KeyError:
    274          # aha, this is the first one :-)
    275          memo[id(memo)]=[x]
    276
    277 -def _deepcopy_inst(x, memo):
    278      if hasattr(x, '__deepcopy__'):
    279          return x.__deepcopy__(memo)
    280      if hasattr(x, '__getinitargs__'):
    281          args = x.__getinitargs__()
    282          args = deepcopy(args, memo)
    283          y = x.__class__(*args)
    284      else:
    285          y = _EmptyClass()
    286          y.__class__ = x.__class__
    287      memo[id(x)] = y
    288      if hasattr(x, '__getstate__'):
    289          state = x.__getstate__()
    290      else:
    291          state = x.__dict__
    292      state = deepcopy(state, memo)
    293      if hasattr(y, '__setstate__'):
    294          y.__setstate__(state)
    295      else:
    296          y.__dict__.update(state)
    297      return y
    298  d[types.InstanceType] = _deepcopy_inst
    299
    300 -def _reconstruct(x, info, deep, memo=None):
    301      if isinstance(info, str):
    302          return x
    303      assert isinstance(info, tuple)
    304      if memo is None:
    305          memo = {}
    306      n = len(info)
    307      assert n in (2, 3, 4, 5)
    308      callable, args = info[:2]
    309      if n > 2:
    310          state = info[2]
    311      else:
    312          state = {}
    313      if n > 3:
    314          listiter = info[3]
    315      else:
    316          listiter = None
    317      if n > 4:
    318          dictiter = info[4]
    319      else:
    320          dictiter = None
    321      if deep:
    322          args = deepcopy(args, memo)
    323      y = callable(*args)
    324      memo[id(x)] = y
    325      if listiter is not None:
    326          for item in listiter:
    327              if deep:
    328                  item = deepcopy(item, memo)
    329              y.append(item)
    330      if dictiter is not None:
    331          for key, value in dictiter:
    332              if deep:
    333                  key = deepcopy(key, memo)
    334                  value = deepcopy(value, memo)
    335              y[key] = value
    336      if state:
    337          if deep:
    338              state = deepcopy(state, memo)
    339          if hasattr(y, '__setstate__'):
    340              y.__setstate__(state)
    341          else:
    342              if isinstance(state, tuple) and len(state) == 2:
    343                  state, slotstate = state
    344              else:
    345                  slotstate = None
    346              if state is not None:
    347                  y.__dict__.update(state)
    348              if slotstate is not None:
    349                  for key, value in slotstate.iteritems():
    350                      setattr(y, key, value)
    351      return y
    352
    353  del d
    354
    355  del types
    356
    357  # Helper for instance creation without calling __init__
    358 -class _EmptyClass:
    359      pass
    360
    361 -def _test():
    362      l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'],
    363           {'abc': 'ABC'}, (), [], {}]
    364      l1 = copy(l)
    365      print l1==l
    366      l1 = map(copy, l)
    367      print l1==l
    368      l1 = deepcopy(l)
    369      print l1==l
    370      class C:
    371          def __init__(self, arg=None):
    372              self.a = 1
    373              self.arg = arg
    374              if __name__ == '__main__':
    375                  import sys
    376                  file = sys.argv[0]
    377              else:
    378                  file = __file__
    379              self.fp = open(file)
    380              self.fp.close()
    381          def __getstate__(self):
    382              return {'a': self.a, 'arg': self.arg}
    383          def __setstate__(self, state):
    384              for key, value in state.iteritems():
    385                  setattr(self, key, value)
    386          def __deepcopy__(self, memo=None):
    387              new = self.__class__(deepcopy(self.arg, memo))
    388              new.a = self.a
    389              return new
    390      c = C('argument sketch')
    391      l.append(c)
    392      l2 = copy(l)
    393      print l == l2
    394      print l
    395      print l2
    396      l2 = deepcopy(l)
    397      print l == l2
    398      print l
    399      print l2
    400      l.append({l[1]: l, 'xyz': l[2]})
    401      l3 = copy(l)
    402      import repr
    403      print map(repr.repr, l)
    404      print map(repr.repr, l1)
    405      print map(repr.repr, l2)
    406      print map(repr.repr, l3)
    407      l3 = deepcopy(l)
    408      import repr
    409      print map(repr.repr, l)
    410      print map(repr.repr, l1)
    411      print map(repr.repr, l2)
    412      print map(repr.repr, l3)
    413
    414  if __name__ == '__main__':
    415      _test()
    416