Passed
Push — main ( 2b775d...83a9fb )
by Douglas
04:59 queued 02:43
created

mandos.model.utils.DisjointEnum.__new__()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
import enum
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
from typing import Optional, Union, AbstractSet
3
4
from pocketutils.core.exceptions import XValueError
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.exceptions'
Loading history...
5
6
from mandos.model.utils.setup import logger
7
8
9
class DisjointEnum(enum.Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
10
    @classmethod
11
    def _fix_lookup(cls, s: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
12
        return s
13
14
    @classmethod
15
    def or_none(cls, s: Union[str, __qualname__]) -> Optional[__qualname__]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
16
        try:
17
            return cls.of(s)
18
        except KeyError:
19
            return None
20
21
    @classmethod
22
    def of(cls, s: Union[str, __qualname__]) -> __qualname__:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
Coding Style Naming introduced by
Method name "of" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
23
        if isinstance(s, cls):
24
            return s
25
        return cls[cls._fix_lookup(s)]
26
27
    def __new__(cls, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
Unused Code introduced by
The argument args seems to be unused.
Loading history...
28
        value = len(cls.__members__) + 1
29
        obj = object.__new__(cls)
30
        obj._value_ = value
31
        return obj
32
33
    def __repr__(self):
0 ignored issues
show
introduced by
__repr__ does not return str
Loading history...
34
        return self.name
35
36
    def __str__(self):
0 ignored issues
show
introduced by
__str__ does not return str
Loading history...
37
        return self.name
38
39
40
class FlagEnum(enum.Flag):
41
    """
42
    A bit flag that behaves as a set, has a null set, and auto-sets values and names.
43
44
    Example:
45
        .. code-block::
46
            class Flavor(FlagEnum):
47
                none = ()
48
                bitter = ()
49
                sweet = ()
50
                sour = ()
51
                umami = ()
52
            bittersweet = Flavor.bitter | Flavor.sweet
53
            print(bittersweet.value)  # 1 + 2 == 3
54
            print(bittersweet.name)   # "bitter|sweet"
55
56
    .. important::
57
        The *first element* must always be the null set ("no flags")
58
        and should be named something like 'none', 'empty', or 'zero'
59
    """
60
61
    @classmethod
62
    def _fix_lookup(cls, s: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
63
        return s
64
65
    def __new__(cls, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument args seems to be unused.
Loading history...
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
66
        if len(cls.__members__) == 0:
67
            value = 0
68
        else:
69
            value = 2 ** (len(cls.__members__) - 1)
70
        obj = object.__new__(cls)
71
        obj._value_ = value
72
        return obj
73
74
    @classmethod
75
    def _create_pseudo_member_(cls, value):
76
        value = super()._create_pseudo_member_(value)
77
        members, _ = enum._decompose(cls, value)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _decompose was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
78
        value._name_ = "|".join([m.name for m in members])
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _name_ was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
79
        return value
80
81
    @classmethod
82
    def or_none(cls, s: Union[str, __qualname__]) -> Optional[__qualname__]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
83
        try:
84
            return cls.of(s)
85
        except KeyError:
86
            return None
87
88
    @classmethod
89
    def of(cls, s: Union[str, __qualname__, AbstractSet[Union[str, __qualname__]]]) -> __qualname__:
0 ignored issues
show
Coding Style Naming introduced by
Method name "of" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
introduced by
Missing function or method docstring
Loading history...
90
        if isinstance(s, cls):
91
            return s
92
        if isinstance(s, str):
93
            return cls[cls._fix_lookup_(s)]
0 ignored issues
show
Bug introduced by
Class 'FlagEnum' has no '_fix_lookup_' member; maybe '_fix_lookup'?

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
94
        z = cls[0]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "z" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
95
        for m in s:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "m" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
96
            z |= cls.of(m)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "z" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' 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...
97
        return z
98
99
    def __repr__(self):
0 ignored issues
show
introduced by
__repr__ does not return str
Loading history...
100
        return self.name
101
102
    def __str__(self):
0 ignored issues
show
introduced by
__str__ does not return str
Loading history...
103
        return self.name
104
105
106
class TrueFalseUnknown(DisjointEnum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
107
    true = ()
108
    false = ()
109
    unknown = ()
110
111
    @classmethod
112
    def _unmatched_type(cls) -> Optional[__qualname__]:
113
        return cls.unknown
114
115
    @classmethod
116
    def _fix_lookup(cls, s: str) -> str:
117
        s = s.lower().strip()
118
        return dict(t="true", false="false").get(s, s)
119
120
121
class MultiTruth(FlagEnum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
122
    false = ()
123
    true = ()
124
125
126
class CleverEnum(DisjointEnum):
127
    """
128
    An enum with a ``.of`` method that finds values
129
    with limited string/value fixing.
130
    May support an "unmatched" type -- a fallback value when there is no match.
131
    This is similar to pocketutils' simpler ``SmartEnum``.
132
    It is mainly useful for enums corresponding to concepts in ChEMBL and PubChem,
133
    where it's acceptable for the user to input spaces (like the database concepts use)
134
    rather than the underscores that Python requires.
135
    """
136
137
    @classmethod
138
    def of(cls, s: Union[str, __qualname__]) -> __qualname__:
139
        try:
140
            return super().of(s)
141
        except KeyError:
142
            unknown = cls._unmatched_type()
143
            logger.error(f"Value {s} not found. Using {unknown}")
144
            if unknown is None:
145
                raise XValueError(f"Value {s} not found and unmatched_type is None")
146
            return unknown
147
148
    @classmethod
149
    def _unmatched_type(cls) -> Optional[__qualname__]:
150
        return None
151
152
    @classmethod
153
    def _fix_lookup(cls, s: str) -> str:
154
        return s.strip().replace(" ", "_").replace(".", "_").replace("-", "_").lower()
155
156
157
__all__ = ["TrueFalseUnknown", "DisjointEnum", "FlagEnum", "CleverEnum"]
158