Документ взят из кэша поисковой машины. Адрес оригинального документа : http://kodomo.fbb.msu.ru/hg/allpy/diff/afed1fd8920c/allpy/base.py
Дата изменения: Unknown
Дата индексирования: Wed Feb 27 20:38:47 2013
Кодировка:
allpy: allpy/base.py diff

allpy

diff allpy/base.py @ 1091:afed1fd8920c

Added backreferences to `Seqeunce`s from `Monomer`s (closes #49) WARNING! Please note that `Sequence` API almost changed entirely! WARNING! This commit immediately obsoletes classmethods `Monomer.from_code*`, `Monomer.from_name` and `Sequence.from_monomers`. Turns out, python can not pickle sets/dicts which have keys, which inderecly reference the set/dict itself: http://bugs.python.org/issue9269 -- which is excatly what we have in abundance after this change. To allow pickling added `__getstate__` to `Monomer` to return all attributes, except `sequence` and `__setstate__` to `Sequence`, which runs through all monomers and returns the `sequence` attribute back to where it belongs. WARNING! This MAY result in unexpected behaviour in some cases. (Which should be rare enough).
author Daniil Alexeyevsky <dendik@kodomo.fbb.msu.ru>
date Sat, 02 Jun 2012 19:33:42 +0400
parents 4b1a6a7bbeea
children 41a167bbf150
line diff
     1.1 --- a/allpy/base.py	Sat Jun 02 19:29:40 2012 +0400
     1.2 +++ b/allpy/base.py	Sat Jun 02 19:33:42 2012 +0400
     1.3 @@ -29,6 +29,9 @@
     1.4      by_name = {}
     1.5      """A mapping from full monomer name to Monomer subclass."""
     1.6  
     1.7 +    sequence = None
     1.8 +    """A sequence the monomer belongs to."""
     1.9 +
    1.10      @classmethod
    1.11      def _subclass(cls, name='', code1='', code3='', is_modified=False):
    1.12          """Create new subclass of Monomer for given monomer type."""
    1.13 @@ -67,23 +70,6 @@
    1.14          for code1, is_modified, code3, name in codes:
    1.15              cls._subclass(name, code1, code3, is_modified)
    1.16  
    1.17 -    @classmethod
    1.18 -    def from_code1(cls, code1):
    1.19 -        """Create new monomer from 1-letter code."""
    1.20 -        monomer = cls.by_code1[code1.upper()]()
    1.21 -        monomer.input_code1 = code1
    1.22 -        return monomer
    1.23 -
    1.24 -    @classmethod
    1.25 -    def from_code3(cls, code3):
    1.26 -        """Create new monomer from 3-letter code."""
    1.27 -        return cls.by_code3[code3.upper()]()
    1.28 -
    1.29 -    @classmethod
    1.30 -    def from_name(cls, name):
    1.31 -        """Create new monomer from full name."""
    1.32 -        return cls.by_name[name.strip().capitalize()]()
    1.33 -
    1.34      def __repr__(self):
    1.35          return "<Monomer %s>"  % str(self.code1)
    1.36  
    1.37 @@ -101,6 +87,30 @@
    1.38      def __ne__(self, other):
    1.39          return not (self == other)
    1.40  
    1.41 +    def __getstate__(self):
    1.42 +        """Overcome difficulties with pickle.
    1.43 +
    1.44 +        Pickle is unable to store `set`s/`dict`s that have objects referencing
    1.45 +        back the `set`/`dict` itself, which `sequence` in monomer does.
    1.46 +        ( http://bugs.python.org/issue9269 )
    1.47 +
    1.48 +        To sidestep the bug we store the monomer WITHOUT `sequence` attribute.
    1.49 +
    1.50 +        See also `Sequence.__setstate__`.
    1.51 +        """
    1.52 +        state = {}
    1.53 +        state.update(vars(self))
    1.54 +        if 'sequence' in state:
    1.55 +            del state['sequence']
    1.56 +        return state
    1.57 +
    1.58 +    def _obsolete_method(cls, *args, **kws):
    1.59 +        """OBSOLETE"""
    1.60 +        raise AttributeError("Call to obsolete method.")
    1.61 +    from_code1 = classmethod(_obsolete_method)
    1.62 +    from_code3 = classmethod(_obsolete_method)
    1.63 +    from_name = classmethod(_obsolete_method)
    1.64 +
    1.65  class MarkupContainerMixin(object):
    1.66      """Common functions for alignment and sequence for dealing with markups.
    1.67      """
    1.68 @@ -162,31 +172,45 @@
    1.69      """Description of object kind."""
    1.70  
    1.71      name = ''
    1.72 +    """Squence identifier."""
    1.73 +
    1.74      description = ''
    1.75 +    """Detailed sequence description."""
    1.76 +
    1.77      source = ''
    1.78 +    """Sequence source."""
    1.79  
    1.80 -    def __init__(self, *args):
    1.81 -        list.__init__(self, *args)
    1.82 +    def __init__(self, sequence=(), name='', description='', source=''):
    1.83 +        list.__init__(self, sequence)
    1.84          MarkupContainerMixin._init(self)
    1.85  
    1.86 -    @classmethod
    1.87 -    def from_monomers(cls, monomers=[], name=None, description=None, source=None):
    1.88 -        """Create sequence from a list of monomer objecst."""
    1.89 -        result = cls(monomers)
    1.90 -        if name:
    1.91 -            result.name = name
    1.92 -        if description:
    1.93 -            result.description = description
    1.94 -        if source:
    1.95 -            result.source = source
    1.96 -        return result
    1.97 +        self.name = name
    1.98 +        self.description = description
    1.99 +        self.source = source
   1.100 +
   1.101 +    def append_monomer(self, code1=None, code3=None, name=None):
   1.102 +        """Append a new monomer to the sequence. Return the new monomer."""
   1.103 +        assert bool(code1) + bool(code3) + bool(name) == 1, \
   1.104 +            "Please specify exactly one of: code1, code3, name"
   1.105 +        if code1:
   1.106 +            cls = self.types.Monomer.by_code1[code1.upper()]
   1.107 +        elif code3:
   1.108 +            cls = self.types.Monomer.by_code3[code3.upper()]
   1.109 +        elif name:
   1.110 +            cls = self.types.Monomer.by_name[name.strip().capitalize()]
   1.111 +        monomer = cls()
   1.112 +        monomer.sequence = self
   1.113 +        monomer.input_code1 = code1
   1.114 +        self.append(monomer)
   1.115 +        return monomer
   1.116  
   1.117      @classmethod
   1.118      def from_string(cls, string, name='', description='', source=''):
   1.119          """Create sequences from string of one-letter codes."""
   1.120 -        monomer = cls.types.Monomer.from_code1
   1.121 -        monomers = [monomer(letter) for letter in string]
   1.122 -        return cls.from_monomers(monomers, name, description, source)
   1.123 +        self = cls([], name=name, description=description, source=source)
   1.124 +        for letter in string:
   1.125 +            self.append_monomer(code1=letter)
   1.126 +        return self
   1.127  
   1.128      def __repr__(self):
   1.129          if self.name:
   1.130 @@ -202,6 +226,26 @@
   1.131          """Hash sequence by identity."""
   1.132          return id(self)
   1.133  
   1.134 +    def __setstate__(self, state):
   1.135 +        """Overcome difficulties with pickle: add `monomer.sequence` after loading.
   1.136 +
   1.137 +        Pickle is unable to store `set`s/`dict`s that have objects referencing
   1.138 +        back the `set`/`dict` itself, which `sequence` in monomer does.
   1.139 +        ( http://bugs.python.org/issue9269 )
   1.140 +
   1.141 +        To sidestep the bug we store the monomer WITHOUT `sequence` attribute.
   1.142 +
   1.143 +        See also `Monomer.__getstate__`.
   1.144 +        """
   1.145 +        vars(self).update(state)
   1.146 +        for monomer in self:
   1.147 +            monomer.sequence = self
   1.148 +
   1.149 +    @classmethod
   1.150 +    def from_monomers(cls, *args, **kws):
   1.151 +        """OBSOLETE."""
   1.152 +        raise AttributeError("Sequence.from_monomers is obsolete")
   1.153 +
   1.154  class Alignment(MarkupContainerMixin):
   1.155      """Alignment. It is a list of Columns."""
   1.156