source: orange-bioinformatics/orangecontrib/bio/obiKEGG/entry/__init__.py @ 1873:0810c5708cc5

Revision 1873:0810c5708cc5, 4.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 6 months ago (diff)

Moved '_bioinformatics' into orangecontrib namespace.

Line 
1"""
2DBGET entry
3"""
4from __future__ import absolute_import
5
6__all__ = ["parser", "fields"]
7
8from collections import defaultdict
9
10from . import fields
11from .parser import DBGETEntryParser
12
13
14# TODO: Remove the use of entry_decorate decorator
15# for constructing a DBEntry subclass, make fields
16# properties with __get__ method, and explicit assignment
17# and meaningful docstrings
18
19
20def entry_decorate(cls):
21    """
22    Decorate the DBEntry subclass with properties for accessing
23    the fields through the 'DBField._convert' interface
24
25    """
26    reserved_names_map = {"class": "class_", "def": "def_"}
27
28    def construct_one(name):
29        def get(self):
30            field = getattr(self, name, None)
31            if field is not None:
32                return field._convert()
33            else:
34                return None
35        return property(get, doc=name)
36
37    def construct_multiple(name):
38        def get(self):
39            field = getattr(self, name, None)
40            if field is not None:
41                return [f._convert() for f in field]
42            else:
43                return None
44        return property(get, doc=name)
45
46    for name, field in cls.FIELDS:
47        name_lower = name.lower()
48        if not hasattr(cls, name_lower):
49            if name in cls.MULTIPLE_FIELDS:
50                prop = construct_multiple(name)
51            else:
52                prop = construct_one(name)
53            setattr(cls, reserved_names_map.get(name_lower, name_lower), prop)
54
55    return cls
56
57
58class DBEntry(object):
59    """
60    A DBGET entry object.
61    """
62    FIELDS = [("ENTRY", fields.DBEntryField)]
63    MULTIPLE_FIELDS = []
64
65    def __init__(self, text=None):
66        self._sections = {}
67        if text is not None:
68            self.parse(text)
69
70    @property
71    def entry_key(self):
72        """
73        Primary entry key used for identifying the entry.
74        """
75        return self.entry.split(" ", 1)[0]
76
77    def parse(self, text):
78        """
79        Parse `text` string containing a formated DBGET entry.
80        """
81        parser = DBGETEntryParser()
82        gen = parser.parse_string(text)
83        field_constructors = dict(self.FIELDS)
84
85        current = None
86        current_subfield = None
87        entry_fields = []
88        for (event, title, text) in gen:
89            if event == DBGETEntryParser.SECTION_START:
90                if title in field_constructors:
91                    ftype = field_constructors[title]
92                else:
93                    ftype = fields.DBSimpleField
94                current = ftype(text)
95                if current.TITLE is None:
96                    current.TITLE = title
97            elif event == DBGETEntryParser.SECTION_END:
98                entry_fields.append(current)
99                current = None
100            elif event == DBGETEntryParser.SUBSECTION_START:
101                current_subfield = fields.DBSimpleField(text)
102                current_subfield.TITLE = title
103                if not isinstance(current, fields.DBFieldWithSubsections):
104                    # Upgrade simple fields to FieldWithSubsection
105                    new = fields.DBFieldWithSubsections(current.text)
106                    new.TITLE = current.TITLE
107                    current = new
108
109            elif event == DBGETEntryParser.SUBSECTION_END:
110                current.subsections.append(current_subfield)
111                current_subfield = None
112            elif event == DBGETEntryParser.TEXT:
113                if current_subfield is not None:
114                    current_subfield.text += text
115                elif current is not None:
116                    current.text += text
117            elif event == DBGETEntryParser.ENTRY_END:
118                break
119
120        self.fields = entry_fields
121        self._consolidate()
122
123    def _consolidate(self):
124        """
125        Update mapping to field entries.
126        """
127        registered_fields = dict(self.FIELDS)
128        multiple_fields = set(self.MULTIPLE_FIELDS)
129
130        for field in self.fields:
131            title = field.TITLE
132            if title not in registered_fields:
133                import warnings
134                warnings.warn("Nonregisterd field %r in %r" % \
135                              (title, type(self)))
136
137            if title in multiple_fields:
138                if not hasattr(self, title):
139                    setattr(self, title, [])
140                getattr(self, title).append(field)
141            else:
142                setattr(self, title, field)
143
144    def __str__(self):
145        return self.format()
146
147    def format(self, section_indent=12):
148        """
149        Return a DBGET formated string representation.
150        """
151        return "".join(f.format(section_indent)
152                       for f in self.fields)
153
154    def get(self, key, default=None):
155        raise NotImplementedError
156
157        f = getattr(self, key, None)
158        if f is not None:
159            if key in self.MULTIPLE_FIELDS:
160                return [f.text for f in f]
161            else:
162                return f.text
163        else:
164            return None
Note: See TracBrowser for help on using the repository browser.