Passed
Push — main ( 83a9fb...fa90c4 )
by Douglas
03:43
created

mandos.model.searches.Search._create_hit()   A

Complexity

Conditions 1

Size

Total Lines 33
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 29
nop 10
dl 0
loc 33
rs 9.184
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
from __future__ import annotations
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
3
import abc
4
import dataclasses
5
from typing import Any, Generic, Mapping, Sequence, TypeVar
6
7
from pocketutils.core.exceptions import XTypeError
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.exceptions'
Loading history...
8
from suretime import Suretime
0 ignored issues
show
introduced by
Unable to import 'suretime'
Loading history...
9
10
from mandos.model.hit_dfs import HitDf
11
from mandos.model.hits import AbstractHit
12
from mandos.model.utils.reflection_utils import ReflectionUtils
13
from mandos.model.utils.resources import MandosResources
14
15
H = TypeVar("H", bound=AbstractHit, covariant=True)
0 ignored issues
show
Coding Style Naming introduced by
Class name "H" 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...
16
17
18
class SearchError(Exception):
19
    """
20
    Wrapper for any exception raised in ``find`` except for ``CompoundNotFoundError``.
21
    """
22
23
    def __init__(
24
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
25
        *args,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
26
        inchikey: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
27
        search_key: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
28
        search_class: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
29
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
30
    ):
31
        super().__init__(*args, *kwargs)
32
        self.inchikey = inchikey
33
        self.search_key = search_key
34
        self.search_class = search_class
35
36
37
class Search(Generic[H], metaclass=abc.ABCMeta):
38
    """
39
    Something to search and how to do it.
40
    """
41
42
    def __init__(self, key: str):
43
        self.key = key
44
45
    @classmethod
46
    def primary_data_source(cls) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
47
        z = MandosResources.strings[cls.__name__]["source"]
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...
48
        # TODO: really?
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
49
        return z.split(":")[0]
50
51
    @property
52
    def search_class(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
53
        return self.__class__.__name__
54
55
    @classmethod
56
    def search_name(cls) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
57
        return cls.__name__.lower().replace("search", "")
58
59
    def get_params(self) -> Mapping[str, Any]:
60
        """
61
        Returns the *parameters* of this ``Search`` their values.
62
        Parameters are attributes that do not begin with an underscore.
63
        """
64
        return {key: value for key, value in vars(self).items() if not key.startswith("_")}
65
66
    def find(self, inchikey: str) -> Sequence[H]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
67
        # override this
68
        raise NotImplementedError()
69
70
    @classmethod
71
    def hit_fields(cls) -> Sequence[str]:
72
        """
73
        Gets the fields in the Hit type parameter.
74
        """
75
        # Okay, there's a lot of magic going on here
76
        # We need to access the _parameter_ H on cls -- raw `H` doesn't work
77
        # get_args and __orig_bases__ do this for us
78
        # then dataclasses.fields gives us the dataclass fields
79
        # there's also actual_h.__annotations__, but that doesn't include ClassVar and InitVar
80
        # (not that we're using those)
81
        # If this magic is too magical, we can make this an abstract method
82
        # But that would be a lot of excess code and it might be less modular
83
        x = cls.get_h()
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" 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...
84
        # noinspection PyDataclass
85
        return [f.name for f in dataclasses.fields(x) if f.name != "search_class"]
86
87
    @classmethod
88
    def get_h(cls):
89
        """
90
        Returns the underlying hit TypeVar, ``H``.
91
        """
92
        # noinspection PyTypeChecker
93
        return ReflectionUtils.get_generic_arg(cls, AbstractHit)
94
95
    def _format_source(self, **kwargs) -> str:
96
        s = MandosResources.strings[self.search_class]["source"]
0 ignored issues
show
Coding Style Naming introduced by
Variable 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...
97
        for k, v in kwargs.items():
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" 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...
98
            s = s.replace(f"{{{k}}}", str(v))
0 ignored issues
show
Coding Style Naming introduced by
Variable 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...
99
        return s
100
101
    def _format_predicate(self, **kwargs) -> str:
102
        s = MandosResources.strings[self.search_class]["predicate"]
0 ignored issues
show
Coding Style Naming introduced by
Variable 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...
103
        for k, v in kwargs.items():
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" 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...
104
            s = s.replace(f"{{{k}}}", str(v))
0 ignored issues
show
Coding Style Naming introduced by
Variable 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...
105
        return s
106
107
    def _create_hit(
0 ignored issues
show
best-practice introduced by
Too many arguments (9/5)
Loading history...
108
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
109
        c_origin: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
110
        c_matched: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
111
        c_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
112
        c_name: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
113
        data_source: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
114
        predicate: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
115
        object_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
116
        object_name: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
117
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
118
    ) -> H:
119
        # ignore statement -- we've removed it for now
120
        entry = dict(
121
            record_id=None,
122
            search_key=self.key,
123
            search_class=self.search_class,
124
            data_source=data_source,
125
            run_date=Suretime.tagged.now_utc_sys().iso_with_zone,
126
            cache_date=None,
127
            weight=1,
128
            compound_id=c_id,
129
            origin_inchikey=c_origin,
130
            matched_inchikey=c_matched,
131
            compound_name=c_name,
132
            predicate=predicate,
133
            object_id=object_id,
134
            object_name=object_name,
135
        )
136
        entry.update(kwargs)
137
        clazz = self.__class__.get_h()
138
        # noinspection PyArgumentList
139
        return clazz(**entry)
140
141
    def __repr__(self) -> str:
142
        return ", ".join([k + "=" + str(v) for k, v in self.get_params().items()])
143
144
    def __str__(self) -> str:
145
        return repr(self)
146
147
    def __eq__(self, other: Search) -> bool:
148
        """
149
        Returns True iff all of the parameters match, thereby excluding attributes with underscores.
150
        Multiversal equality.
151
152
        Raises:
153
            TypeError: If ``other`` is not a :class:`Search`
154
        """
155
        if not isinstance(other, Search):
156
            raise XTypeError(f"{type(other)} not comparable")
157
        return repr(self) == repr(other)
158
159
160
__all__ = ["Search", "HitDf", "SearchError"]
161