File: __init__.py

package info (click to toggle)
python-ihm 2.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,368 kB
  • sloc: python: 30,422; ansic: 5,990; sh: 24; makefile: 20
file content (188 lines) | stat: -rw-r--r-- 5,974 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
"""Utility classes"""

import string
import os
import ihm
import datetime


class _AsymIDs:
    """Map indices to multi-character asym (chain) IDs.
       We label the first 26 chains A-Z, then we move to two-letter
       chain IDs: AA through AZ, then BA through BZ, through to ZZ.
       This continues with longer chain IDs."""
    def __getitem__(self, ind):
        chars = string.ascii_uppercase
        lc = len(chars)
        ids = []
        while ind >= lc:
            ids.append(chars[ind % lc])
            ind = ind // lc - 1
        ids.append(chars[ind])
        return "".join(reversed(ids))


def _remove_id(obj, attr='_id'):
    """Remove any unique ID from obj"""
    if hasattr(obj, attr):
        delattr(obj, attr)


def _assign_id(obj, seen_objs, obj_by_id, attr='_id', seen_obj=None,
               by_id_obj=None):
    """Assign a unique ID to obj, and track all ids in obj_by_id."""
    if seen_obj is None:
        seen_obj = obj
    if by_id_obj is None:
        by_id_obj = obj
    if seen_obj not in seen_objs:
        if not hasattr(obj, attr):
            obj_by_id.append(by_id_obj)
            setattr(obj, attr, len(obj_by_id))
        seen_objs[seen_obj] = getattr(obj, attr)
    else:
        setattr(obj, attr, seen_objs[seen_obj])


def _get_relative_path(reference, path):
    """Return `path` interpreted relative to `reference`"""
    if os.path.isabs(path):
        return path
    else:
        return os.path.join(os.path.dirname(reference), path)


def _text_choice_property(attr, choices, doc=None):
    """Like `property` but requires that the value be one of the set choices"""
    schoices = frozenset(choices)

    def getfunc(obj):
        return getattr(obj, "_" + attr)

    def setfunc(obj, val):
        if val is not None and val is not ihm.unknown and val not in schoices:
            raise ValueError(
                "Invalid choice %s for %s; valid values are %s, "
                "None, ihm.unknown"
                % (repr(val), attr, ", ".join(repr(x) for x in choices)))
        setattr(obj, "_" + attr, val)

    return property(getfunc, setfunc, doc=doc)


def _check_residue_range(seq_id_range, entity):
    """Make sure that a residue range is not out of range of its Entity"""
    if not entity or not entity._range_check:
        return
    if seq_id_range[1] < seq_id_range[0]:
        raise ValueError("Range %d-%d is invalid; end is before start"
                         % seq_id_range)
    if (seq_id_range[1] > len(entity.sequence)
            or seq_id_range[0] < 1):
        raise IndexError("Range %d-%d out of range for %s (1-%d)"
                         % (seq_id_range[0], seq_id_range[1],
                            entity, len(entity.sequence)))


def _check_residue(r):
    """Make sure that a residue is not out of range of its Entity"""
    if not r.entity or not r.entity._range_check:
        return
    if r.seq_id > len(r.entity.sequence) or r.seq_id < 1:
        raise IndexError("Residue %d out of range for %s (1-%d)"
                         % (r.seq_id, r.entity, len(r.entity.sequence)))


def _check_transform(t):
    if t.rot_matrix in (None, ihm.unknown):
        raise ValueError("Transformation %s is missing rotation" % t)
    if t.tr_vector in (None, ihm.unknown):
        raise ValueError("Transformation %s is missing translation" % t)


def _invert_ranges(ranges, end, start=1):
    """Given a sorted list of non-overlapping ranges, yield a new list which
       contains every range in the range start-end which was not in the
       original list.  For example, if end=4,
       [(2, 3)] -> [(1, 1), (4, 4)]"""
    for r in ranges:
        if r[0] > start:
            yield (start, r[0] - 1)
        start = r[1] + 1
    if end >= start:
        yield (start, end)


def _pred_ranges(ranges, end):
    """Given a sorted list of non-overlapping ranges, yield a new list which
       covers the range 1-end. Each element in the new list contains a new
       third bool member which is True iff the element was in the original
       list. For example, if end=4,
       [(2, 3)] -> [(1, 1, False), (2, 3, True), (4, 4, False)]"""
    start = 1
    for r in ranges:
        if r[0] > start:
            yield (start, r[0] - 1, False)
        yield (r[0], r[1], True)
        start = r[1] + 1
    if end >= start:
        yield (start, end, False)


def _combine_ranges(ranges):
    """Sort the input ranges and remove any overlaps; yield the result.
       For example, [(8, 10), (1, 2), (3, 4)] -> [(1, 4), (8, 10)]"""
    ranges = sorted(ranges)
    if not ranges:
        return
    current = ranges[0]
    for r in ranges[1:]:
        if current[1] + 1 >= r[0]:
            current = (current[0], max(r[1], current[1]))
        else:
            yield current
            current = r
    yield current


def _make_range_from_list(rr):
    """Yield a list of ranges given a sorted list of values.
       For example, [1, 2, 5, 6] -> [[1, 2], [5, 6]]"""
    if not rr:
        return
    current = [rr[0], rr[0]]
    for r in rr[1:]:
        if current[1] + 1 == r:
            current[1] = r
        else:
            yield current
            current = [r, r]
    yield current


def _get_codes(codestr):
    """Convert a one-letter-code string into a sequence of individual
       codes"""
    if codestr is None or codestr is ihm.unknown:
        return
    i = 0
    while i < len(codestr):
        # Strip out linebreaks
        if codestr[i] == '\n':
            pass
        elif codestr[i] == '(':
            end = codestr.index(')', i)
            yield codestr[i + 1:end]
            i = end
        else:
            yield codestr[i]
        i += 1


def _get_iso_date(iso_date_str):
    """Get a datetime.date obj for a string in isoformat."""
    if not iso_date_str:
        return iso_date_str
    return datetime.date(int(iso_date_str[0:4]),
                         int(iso_date_str[5:7]),
                         int(iso_date_str[8:10]))