PropertyView.__setitem__()   C
last analyzed

Complexity

Conditions 7

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 25
ccs 19
cts 19
cp 1
rs 5.5
cc 7
crap 7
1
#! /usr/bin/env python
2
#
3
# Copyright (C) 2015-2016 Rich Lewis <[email protected]>
4
# License: 3-clause BSD
5
6 1
"""
7
## skchem.core.base
8
9
Define base classes for scikit chem objects
10
"""
11
12 1
from abc import ABCMeta, abstractmethod
13 1
import warnings
14 1
import numpy as np
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
15 1
import pandas as pd
0 ignored issues
show
Configuration introduced by
The import pandas could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
16
17
18 1
class ChemicalObject(object):
19
20
    """ A mixin for each chemical object in scikit-chem. """
21
22 1
    @classmethod
23
    def from_super(cls, obj):
24
25
        """ Converts the class of an object to this class. """
26
27 1
        obj.__class__ = cls
28 1
        return obj
29
30
31 1
class ChemicalObjectView(object):
32
33
    """ Abstract iterable view of chemical objects.
34
35
    Concrete classes inheriting from it should implement `__getitem__`
36
    and `__len__`.
37
    """
38
39 1
    __metaclass__ = ABCMeta
40
41 1
    def __init__(self, owner):
42
        """ Return a view """
43 1
        self.owner = owner
44
45 1
    @abstractmethod
46
    def __getitem__(self, index):
47
        # subclass call this, then implement the actual get if None
48 1
        if isinstance(index, slice):
49 1
            return self.to_list()[index]
50 1
        if isinstance(index, (list, tuple)) \
51
                and all(isinstance(i, bool) for i in index) \
52
                and len(index) == len(self):
53 1
            return [self[i] for i, ix in enumerate(index) if ix]
54 1
        elif isinstance(index, (list, tuple)):
55 1
            return [self[ix] for ix in index]
56
        else:
57 1
            return None
58
59 1
    @abstractmethod
60
    def __len__(self):  # pragma: no cover
61
        pass
62
63 1
    def __iter__(self):
64 1
        return ChemicalObjectIterator(self)
65
66 1
    def __str__(self):
67 1
        return str(list(str(obj) for obj in self))
68
69 1
    @property
70
    def props(self):
71
        """ Return a property view of the objects in the view. """
72
73 1
        return MolPropertyView(self)
74
75 1
    def to_list(self):
76
        """ Return a list of objects in the view. """
77
78 1
        return list(self)
79
80 1
    def __repr__(self):
81 1
        return '<{class_} values="{values}" at {address}>'.format(
82
            class_=self.__class__.__name__,
83
            values=str(self),
84
            address=hex(id(self)))
85
86
87 1
class ChemicalObjectIterator(object):
88
89
    """  Iterator for chemical object views.  """
90
91 1
    def __init__(self, view):
92
        """ Create an iterator from a chemical object view. """
93 1
        self.view = view
94 1
        self._current = 0
95 1
        self._high = len(self.view)
96
97 1
    def __next__(self):
98 1
        if self._current >= self._high:
99 1
            raise StopIteration
100
        else:
101 1
            self._current += 1
102 1
            return self.view[self._current - 1]
103
104
    # py2 compat
105 1
    next = __next__
106
107
108 1
class View(object):
109
110
    """ View wrapper interface.  Conforms to the dictionary interface.
111
112
    Objects inheriting from this should implement the `keys`, `getitem`,
113
    `setitem` and `delitem` methods."""
114
115 1
    __metaclass__ = ABCMeta
116
117 1
    @abstractmethod
118
    def keys(self):  # pragma: no cover
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
119
        return []
120
121 1
    def get(self, index, default=None):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
122 1
        if index in self.keys():
123 1
            return self[index]
124
        else:
125 1
            return default
126
127 1
    def pop(self, index, default=None):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
128 1
        if default:
129 1
            val = self.get(index, default)
130
        else:
131 1
            val = self[index]
132 1
        self.remove(index)
133 1
        return val
134
135 1
    def clear(self):
136
        """ Remove all properties from the object. """
137 1
        for idx in self.keys():
138 1
            self.remove(idx)
139
140 1
    def items(self):
141
        """ Return an iterable of key, value pairs. """
142
143 1
        return list((k, self[k]) for k in self.keys())
144
145 1
    def remove(self, key):
146
        """ Remove a property from the object. """
147 1
        self.__delitem__(key)
148
149 1
    @abstractmethod
150
    def __getitem__(self, key):  # pragma: no cover
151
        pass
152
153 1
    @abstractmethod
154
    def __setitem__(self, key, value):  # pragma: no cover
155
        pass
156
157 1
    @abstractmethod
158
    def __delitem__(self, key):  # pragma: no cover
159
        pass
160
161 1
    def __iter__(self):
162 1
        return iter(self.keys())
163
164 1
    def __str__(self):
165 1
        return str(self.to_dict())
166
167 1
    def __len__(self):
168 1
        return len(self.keys())
169
170 1
    def __repr__(self):
171 1
        return '<{klass} values="{values}" at {address}>'.format(
172
            klass=self.__class__.__name__,
173
            values=str(self),
174
            address=hex(id(self)))
175
176 1
    def to_dict(self):
177
        """ Return a dict of the properties on the object. """
178 1
        return dict(self)
179
180 1
    def to_series(self):
181
        """ Return a pd.Series of the properties on the object. """
182 1
        return pd.Series(self.to_dict())
183
184
185 1
class PropertyView(View):
186
    """ Property object wrapper.
187
188
     This provides properties for rdkit objects. """
189
190 1
    _reserved = ('origNoImplicit', 'isImplicit')
191
192 1
    def __init__(self, owner):
193
        """ Initialize a PropertyView.
194
195
        Args:
196
            owner(skchem.ChemicalObject):
197
                A chemical object with properties, such as `skchem.Atom`."""
198
199 1
        self._owner = owner
200
201 1
    def keys(self):
202
        """ The available property keys on the object. """
203
204
        # pretend not to know about 'reserved' props
205 1
        return list(k for k in self._owner.GetPropNames()
206
                    if (k[:1] != '_' and k not in self._reserved))
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after if.
Loading history...
207
208 1
    def __getitem__(self, key):
209
210
        # we manually work out if it was a float that was stored, as GetProp
211
        # returns floats and ints set by SetDoubleProp and SetIntProp as strs
212 1
        value = self._owner.GetProp(str(key))
213 1
        try:
214 1
            return int(value)
215 1
        except ValueError:
216 1
            try:
217 1
                return float(value)
218 1
            except ValueError:
219 1
                return value
220
221 1
    def __setitem__(self, key, value):
222
223 1
        if not isinstance(key, str):
224 1
            msg = 'RDKit property keys can only be of type `str`.' \
225
                  '  Using `{key}` as a `str`.'.format(key=key)
226 1
            warnings.warn(msg)
227 1
            key = str(key)
228
229 1
        if key[0] == '_' or key in self._reserved:
230 1
            msg = '`{value}` is a private RDKit property key. '
231 1
            'Using this may have unintended consequences.'.format(value=value)
232 1
            warnings.warn(msg)
233
234 1
        if isinstance(value, str):
235 1
            self._owner.SetProp(key, value)
236 1
        elif isinstance(value, (int, np.int64, np.int32)):
237 1
            self._owner.SetIntProp(key, value)
238 1
        elif isinstance(value, (float, np.float64, np.float32)):
239 1
            self._owner.SetDoubleProp(key, value)
240
        else:
241 1
            msg = 'RDKit property keys can only be `str`, `int` or `float`. '
242 1
            'Using `{value}` as a `str`.'.format(value=value)
243
244 1
            warnings.warn(msg)
245 1
            self._owner.SetProp(key, str(value))
246
247 1
    def __delitem__(self, index):
248 1
        self._owner.ClearProp(index)
249
250
251 1
class MolPropertyView(View):
252
253
    """ Mol property wrapper.
254
255
    This provides properties for the atom and bond views. """
256
257 1
    def __init__(self, obj_view):
258 1
        self._obj_view = obj_view
259
260 1
    def keys(self):
261
        """ The available property keys on the object. """
262
263 1
        res = set()
264 1
        for atom in self._obj_view:
265 1
            res = res.union(set(atom.props.keys()))
266 1
        return list(res)
267
268 1
    def get(self, key, default=None):
269 1
        return pd.Series((a.props.get(key, default) for a in self._obj_view),
270
                         index=self._obj_view.index)
271
272 1
    def __getitem__(self, key):
273 1
        if key not in self.keys():
274 1
            raise KeyError('No atoms have the property set.')
275 1
        return self.get(key, None)
276
277 1
    def __setitem__(self, key, value):
278 1
        if isinstance(value, (pd.Series, dict)):
279 1
            print('got dict or series')
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after print.
Loading history...
280 1
            for idx, val in pd.compat.iteritems(value):
281 1
                self._obj_view[int(idx)].props[key] = val
282
        else:
283 1
            assert len(self._obj_view) == len(value), \
284
                "Must pass same number of values as atoms."
285 1
            for atom, val in zip(self._obj_view, value):
286 1
                atom.props[key] = val
287
288 1
    def __delitem__(self, key):
289 1
        for atom in self._obj_view:
290 1
            atom.props.remove(key)
291
292 1
    def to_frame(self):
293
294
        """ Return a DataFrame of the properties of the view's objects. """
295
296 1
        return pd.DataFrame(dict(self), index=self._obj_view.index)
297
298 1
    def to_dict(self):
299
300
        """ Return a dict of the properties of the view's objects. """
301
302 1
        return {k: v.tolist() for k, v in self.items()}
303
304 1
    def __str__(self):
305
        return str(self.to_dict())
306