1
|
|
|
from __future__ import annotations |
|
|
|
|
2
|
|
|
|
3
|
|
|
import abc |
4
|
|
|
import logging |
5
|
|
|
import enum |
6
|
|
|
from pathlib import Path |
7
|
|
|
from typing import Optional, Union |
8
|
|
|
|
9
|
|
|
from pocketutils.core.dot_dict import NestedDotDict |
|
|
|
|
10
|
|
|
|
11
|
|
|
logger = logging.getLogger("logger") |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
class CompoundNotFoundError(LookupError): |
|
|
|
|
15
|
|
|
"""""" |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
@enum.unique |
19
|
|
|
class CleverEnum(enum.Enum, metaclass=abc.ABCMeta): |
20
|
|
|
""" |
21
|
|
|
An enum with a ``.of`` method that finds values |
22
|
|
|
with limited string/value fixing. |
23
|
|
|
May support an "unmatched" type -- a fallback value when there is no match. |
24
|
|
|
This is similar to pocketutils' simpler ``SmartEnum``. |
25
|
|
|
It is mainly useful for enums corresponding to concepts in ChEMBL and PubChem, |
26
|
|
|
where it's acceptable for the user to input spaces (like the database concepts use) |
27
|
|
|
rather than the underscores that Python requires. |
28
|
|
|
""" |
29
|
|
|
|
30
|
|
|
@classmethod |
31
|
|
|
def _unmatched_type(cls) -> Optional[__qualname__]: |
32
|
|
|
return None |
33
|
|
|
|
34
|
|
|
@classmethod |
35
|
|
|
def of(cls, s: Union[int, str]) -> __qualname__: |
|
|
|
|
36
|
|
|
key = s.replace(" ", "_").replace("-", "_").lower() |
37
|
|
|
try: |
38
|
|
|
if isinstance(s, str): |
|
|
|
|
39
|
|
|
return cls[key] |
40
|
|
|
elif isinstance(key, int): |
41
|
|
|
return cls(key) |
42
|
|
|
else: |
43
|
|
|
raise TypeError(f"Lookup type {type(s)} for value {s} not a str or int") |
44
|
|
|
except KeyError: |
45
|
|
|
unk = cls._unmatched_type() |
46
|
|
|
if unk is None: |
47
|
|
|
raise |
48
|
|
|
logger.error(f"Target type {key} not found. Using TargetType.unknown.") |
|
|
|
|
49
|
|
|
if not isinstance(unk, cls): |
50
|
|
|
raise AssertionError(f"Wrong type {type(unk)} (lookup: {s})") |
51
|
|
|
return unk |
52
|
|
|
|
53
|
|
|
|
54
|
|
|
class MandosResources: |
|
|
|
|
55
|
|
|
@classmethod |
56
|
|
|
def contains(cls, *nodes: Union[Path, str], suffix: Optional[str] = None) -> bool: |
57
|
|
|
"""Returns whether a resource file (or dir) exists.""" |
58
|
|
|
return cls.path(*nodes, suffix=suffix).exists() |
59
|
|
|
|
60
|
|
|
@classmethod |
61
|
|
|
def path(cls, *nodes: Union[Path, str], suffix: Optional[str] = None) -> Path: |
62
|
|
|
"""Gets a path of a test resource file under resources/.""" |
63
|
|
|
path = Path(Path(__file__).parent.parent, "resources", *nodes) |
64
|
|
|
return path.with_suffix(path.suffix if suffix is None else suffix) |
65
|
|
|
|
66
|
|
|
@classmethod |
67
|
|
|
def json(cls, *nodes: Union[Path, str], suffix: Optional[str] = None) -> NestedDotDict: |
|
|
|
|
68
|
|
|
return NestedDotDict.read_json(cls.path(*nodes, suffix=suffix)) |
69
|
|
|
|
70
|
|
|
|
71
|
|
|
__all__ = ["CompoundNotFoundError", "MandosResources", "CleverEnum"] |
72
|
|
|
|