dmyersturnbull /
mandos
| 1 | """ |
||
| 2 | An abstraction for the ChEMBL REST API. |
||
| 3 | Designed to facilitate testing, but also improves static type checking. |
||
| 4 | """ |
||
| 5 | from __future__ import annotations |
||
| 6 | |||
| 7 | import abc |
||
| 8 | import logging |
||
| 9 | from typing import Any, Callable, Iterator, Mapping, Optional, Sequence, Set, List, Union, Iterable |
||
|
0 ignored issues
–
show
Unused Code
introduced
by
Loading history...
|
|||
| 10 | |||
| 11 | from pocketutils.core.dot_dict import NestedDotDict |
||
|
0 ignored issues
–
show
|
|||
| 12 | |||
| 13 | logger = logging.getLogger("mandos") |
||
| 14 | |||
| 15 | from mandos import MandosResources |
||
|
0 ignored issues
–
show
|
|||
| 16 | |||
| 17 | |||
| 18 | class ChemblFilterQuery(metaclass=abc.ABCMeta): |
||
| 19 | """ |
||
| 20 | Wraps the result of calling ``filter`` on a ChEMBL query. |
||
| 21 | Supports iterating over results (``__iter__`), getting a single item (``__getitem__`), and calling ``only(lst)``. |
||
|
0 ignored issues
–
show
|
|||
| 22 | """ |
||
| 23 | |||
| 24 | def only(self, items: Sequence[str]) -> ChemblFilterQuery: |
||
| 25 | """ |
||
| 26 | Turns this into a query for a single record. |
||
| 27 | |||
| 28 | Args: |
||
| 29 | items: |
||
| 30 | |||
| 31 | Returns: |
||
| 32 | |||
| 33 | """ |
||
| 34 | raise NotImplementedError() |
||
| 35 | |||
| 36 | def __getitem__(self, item: int) -> NestedDotDict: |
||
| 37 | raise NotImplementedError() |
||
| 38 | |||
| 39 | def __len__(self) -> int: |
||
| 40 | raise NotImplementedError() |
||
| 41 | |||
| 42 | def __iter__(self) -> Iterator[NestedDotDict]: |
||
| 43 | raise NotImplementedError() |
||
| 44 | |||
| 45 | @classmethod |
||
| 46 | def mock(cls, items: Sequence[dict]): |
||
| 47 | """ |
||
| 48 | Mocks. |
||
| 49 | |||
| 50 | Args: |
||
| 51 | items: |
||
| 52 | |||
| 53 | Returns: |
||
| 54 | |||
| 55 | """ |
||
| 56 | |||
| 57 | class F(ChemblFilterQuery): |
||
|
0 ignored issues
–
show
Class name "F" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 58 | def only(self, _: Sequence[str]) -> ChemblFilterQuery: |
||
| 59 | return self |
||
| 60 | |||
| 61 | def __getitem__(self, item: int) -> NestedDotDict: |
||
| 62 | return NestedDotDict(items[item]) |
||
| 63 | |||
| 64 | def __len__(self) -> int: |
||
| 65 | return len(items) |
||
| 66 | |||
| 67 | def __iter__(self) -> Iterator[NestedDotDict]: |
||
| 68 | return iter([NestedDotDict(x) for x in items]) |
||
| 69 | |||
| 70 | return F() |
||
| 71 | |||
| 72 | @classmethod |
||
| 73 | def wrap(cls, query): |
||
| 74 | """ |
||
| 75 | Wraps. |
||
| 76 | |||
| 77 | Args: |
||
| 78 | query: |
||
| 79 | |||
| 80 | Returns: |
||
| 81 | |||
| 82 | """ |
||
| 83 | |||
| 84 | class F(ChemblFilterQuery): |
||
|
0 ignored issues
–
show
Class name "F" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 85 | def only(self, items: Sequence[str]) -> ChemblFilterQuery: |
||
| 86 | # TODO technically not returning this |
||
|
0 ignored issues
–
show
|
|||
| 87 | return getattr(query, "only")(items) |
||
| 88 | |||
| 89 | def __getitem__(self, item: int) -> NestedDotDict: |
||
| 90 | return NestedDotDict(query[item]) |
||
| 91 | |||
| 92 | def __len__(self) -> int: |
||
| 93 | return len(query) |
||
| 94 | |||
| 95 | def __iter__(self) -> Iterator[NestedDotDict]: |
||
| 96 | return iter([NestedDotDict(x) for x in query]) |
||
| 97 | |||
| 98 | return F() |
||
| 99 | |||
| 100 | |||
| 101 | class ChemblEntrypoint: |
||
| 102 | """ |
||
| 103 | Wraps just part of a node in the ChEMBL REST API. |
||
| 104 | Ex ``Chembl.target``. |
||
| 105 | """ |
||
| 106 | |||
| 107 | def filter(self, **kwargs) -> ChemblFilterQuery: |
||
|
0 ignored issues
–
show
|
|||
| 108 | raise NotImplementedError() |
||
| 109 | |||
| 110 | def get(self, arg: str) -> Optional[NestedDotDict]: |
||
|
0 ignored issues
–
show
|
|||
| 111 | raise NotImplementedError() |
||
| 112 | |||
| 113 | @classmethod |
||
| 114 | def mock( |
||
| 115 | cls, |
||
|
0 ignored issues
–
show
|
|||
| 116 | get_items: Mapping[str, dict], |
||
|
0 ignored issues
–
show
|
|||
| 117 | filter_items: Optional[Callable[[Mapping[str, Any]], Sequence[dict]]] = None, |
||
|
0 ignored issues
–
show
|
|||
| 118 | ) -> ChemblEntrypoint: |
||
| 119 | """ |
||
| 120 | |||
| 121 | Args: |
||
| 122 | get_items: Map from single arg for calling ``get`` to the item to return |
||
| 123 | filter_items: Map from kwarg-set for calling ``filter`` to the list of items to return; |
||
| 124 | If None, returns ``items`` in all cases |
||
| 125 | |||
| 126 | Returns: |
||
| 127 | |||
| 128 | """ |
||
| 129 | |||
| 130 | class X(ChemblEntrypoint): |
||
|
0 ignored issues
–
show
Class name "X" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 131 | def filter(self, **kwargs) -> ChemblFilterQuery: |
||
| 132 | if filter_items is None: |
||
| 133 | return ChemblFilterQuery.mock(list(get_items.values())) |
||
| 134 | items = filter_items(kwargs) |
||
| 135 | return ChemblFilterQuery.mock(items) |
||
| 136 | |||
| 137 | def get(self, arg: str) -> Optional[NestedDotDict]: |
||
| 138 | return NestedDotDict(get_items[arg]) |
||
| 139 | |||
| 140 | return X() |
||
| 141 | |||
| 142 | @classmethod |
||
| 143 | def wrap(cls, obj) -> ChemblEntrypoint: |
||
| 144 | """ |
||
| 145 | |||
| 146 | Args: |
||
| 147 | obj: |
||
| 148 | |||
| 149 | Returns: |
||
| 150 | |||
| 151 | """ |
||
| 152 | |||
| 153 | class X(ChemblEntrypoint): |
||
|
0 ignored issues
–
show
Class name "X" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 154 | def filter(self, **kwargs) -> ChemblFilterQuery: |
||
| 155 | query = getattr(obj, "filter")(**kwargs) |
||
| 156 | return ChemblFilterQuery.wrap(query) |
||
| 157 | |||
| 158 | def get(self, arg: str) -> Optional[NestedDotDict]: |
||
| 159 | return NestedDotDict(getattr(obj, "get")(arg)) |
||
| 160 | |||
| 161 | return X() |
||
| 162 | |||
| 163 | |||
| 164 | class ChemblApi(metaclass=abc.ABCMeta): |
||
| 165 | """ |
||
| 166 | Wraps the whole ChEMBL API. |
||
| 167 | """ |
||
| 168 | |||
| 169 | @property |
||
| 170 | def activity(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 171 | return self.__dict__["activity"] |
||
| 172 | |||
| 173 | @property |
||
| 174 | def assay(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 175 | return self.__dict__["assay"] |
||
| 176 | |||
| 177 | @property |
||
| 178 | def atc_class(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 179 | return self.__dict__["atc_class"] |
||
| 180 | |||
| 181 | @property |
||
| 182 | def drug(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 183 | return self.__dict__["drug"] |
||
| 184 | |||
| 185 | @property |
||
| 186 | def drug_indication(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 187 | return self.__dict__["drug_indication"] |
||
| 188 | |||
| 189 | @property |
||
| 190 | def mechanism(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 191 | return self.__dict__["mechanism"] |
||
| 192 | |||
| 193 | @property |
||
| 194 | def molecule(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 195 | return self.__dict__["molecule"] |
||
| 196 | |||
| 197 | @property |
||
| 198 | def molecule_form(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 199 | return self.__dict__["molecule_form"] |
||
| 200 | |||
| 201 | @property |
||
| 202 | def organism(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 203 | return self.__dict__["mechanism"] |
||
| 204 | |||
| 205 | @property |
||
| 206 | def go_slim(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 207 | return self.__dict__["go_slim"] |
||
| 208 | |||
| 209 | @property |
||
| 210 | def target(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 211 | return self.__dict__["target"] |
||
| 212 | |||
| 213 | @property |
||
| 214 | def target_relation(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 215 | return self.__dict__["target_relation"] |
||
| 216 | |||
| 217 | @property |
||
| 218 | def protein_class(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 219 | return self.__dict__["protein_class"] |
||
| 220 | |||
| 221 | @property |
||
| 222 | def target_component(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 223 | return self.__dict__["target_component"] |
||
| 224 | |||
| 225 | @property |
||
| 226 | def target_prediction(self) -> ChemblEntrypoint: |
||
|
0 ignored issues
–
show
|
|||
| 227 | return self.__dict__["target_prediction"] |
||
| 228 | |||
| 229 | def __getattribute__(self, item: str) -> ChemblEntrypoint: |
||
| 230 | raise NotImplementedError() |
||
| 231 | |||
| 232 | @classmethod |
||
| 233 | def mock(cls, entrypoints: Mapping[str, ChemblEntrypoint]) -> ChemblApi: |
||
| 234 | """ |
||
| 235 | Mocks. |
||
| 236 | |||
| 237 | Args: |
||
| 238 | entrypoints: |
||
| 239 | |||
| 240 | Returns: |
||
| 241 | |||
| 242 | """ |
||
| 243 | |||
| 244 | class X(ChemblApi): |
||
|
0 ignored issues
–
show
Class name "X" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 245 | def __getattribute__(self, item: str) -> ChemblEntrypoint: |
||
| 246 | return entrypoints[item] |
||
| 247 | |||
| 248 | return X() |
||
| 249 | |||
| 250 | @classmethod |
||
| 251 | def wrap(cls, obj) -> ChemblApi: |
||
| 252 | """ |
||
| 253 | Wraps. |
||
| 254 | |||
| 255 | Args: |
||
| 256 | obj: |
||
| 257 | |||
| 258 | Returns: |
||
| 259 | |||
| 260 | """ |
||
| 261 | |||
| 262 | class X(ChemblApi): |
||
|
0 ignored issues
–
show
Class name "X" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 263 | def __getattribute__(self, item: str) -> ChemblEntrypoint: |
||
| 264 | return ChemblEntrypoint.wrap(getattr(obj, item)) |
||
| 265 | |||
| 266 | return X() |
||
| 267 | |||
| 268 | |||
| 269 | __all__ = [ |
||
| 270 | "ChemblApi", |
||
| 271 | "ChemblEntrypoint", |
||
| 272 | "ChemblFilterQuery", |
||
| 273 | ] |
||
| 274 |