Passed
Push — dependabot/pip/sphinx-copybutt... ( c72176 )
by
unknown
18:24 queued 16:24
created

MultiSearch.__init__()   A

Complexity

Conditions 4

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nop 3
dl 0
loc 17
rs 9.75
c 0
b 0
f 0
1
"""
2
Runner.
3
"""
4
5
from __future__ import annotations
6
7
import typing
8
from pathlib import Path
9
from typing import Optional, Type, Union
0 ignored issues
show
Unused Code introduced by
Unused Optional imported from typing
Loading history...
10
11
import typer
0 ignored issues
show
introduced by
Unable to import 'typer'
Loading history...
12
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.dot_dict'
Loading history...
13
14
from mandos import MandosLogging
0 ignored issues
show
Unused Code introduced by
Unused MandosLogging imported from mandos
Loading history...
15
from mandos.entries import EntryMeta
16
from mandos.entries.api_singletons import Apis
17
from mandos.entries.entries import Entries, Entry
18
from mandos.model import InjectionError, ReflectionUtils
0 ignored issues
show
Unused Code introduced by
Unused ReflectionUtils imported from mandos.model
Loading history...
19
from mandos.model.settings import MANDOS_SETTINGS
20
21
cli = typer.Typer()
22
Apis.set_default()
23
Chembl, Pubchem = Apis.Chembl, Apis.Pubchem
24
25
EntriesByCmd: typing.Dict[str, Type[Entry]] = {e.cmd(): e for e in Entries}
26
27
# these are only permitted in 'meta', not individual searches
28
meta_keys = {"verbose", "quiet", "check", "log", "to"}
29
30
31
class MultiSearch:
32
    """
33
    Ugh.
34
    """
35
36
    # ------------------------
37
    # TODO: This code is nightmarishly complex.
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
38
    # ------------------------
39
40
    def __init__(self, path: Path, config: str):
41
        self.path = path
42
        toml = NestedDotDict.read_toml(config)
43
        self.toml_searches = toml.get_as("search", list, [])
44
        # 'meta' allows us to set defaults
45
        # for now, I'm only allowing true "metadata" keys
46
        self.meta = toml.sub("meta")
47
        # TODO: allow specifying a directory, not just format (suffix)
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
48
        self.format = self.meta.get_as("to", str, MANDOS_SETTINGS.default_table_suffix)
49
        if not self.format.startswith("."):
50
            raise ValueError(
51
                f"Value to='{self.format}' does not start with '.'. Only a filename extension is permitted."
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (108/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
52
            )
53
        for key, value in self.meta.items():
54
            if key not in meta_keys:
55
                raise ValueError(
56
                    f"Found {key}={value} in 'meta' of TOML. Only {', '.join(meta_keys)} are supported."
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
57
                )
58
59
    def search(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
60
        cmds = self._build_commands()
61
        # this is terrible
62
        EntryMeta.set_logging(
63
            quiet=self.meta.get("quiet", False),
64
            verbose=self.meta.get("verbose", False),
65
            log=self.meta.get("log", None),
66
        )
67
        for key, (ent, params) in cmds.items():
0 ignored issues
show
Unused Code introduced by
The variable key seems to be unused.
Loading history...
68
            ent.run(self.path, **{**params, **dict(no_setup=True, check=False)})
69
70
    def _build_commands(
71
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
72
    ) -> typing.Dict[str, typing.Tuple[Type[Entry], typing.Mapping[str, Union[int, str, float]]]]:
73
        commands: typing.Dict[
74
            str, typing.Tuple[Type[Entry], typing.Mapping[str, Union[int, str, float]]]
75
        ] = {}
76
        # build up the lit of Entry classes first, and run ``test`` on each one
77
        # that's to check that the parameters are correct before running anything
78
        for e in self.toml_searches:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "e" 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...
79
            e = NestedDotDict(e)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "e" 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...
80
            cmd = e.req_as("source", str)
81
            key = e.get_as("key", str, cmd)
82
            try:
83
                cmd = EntriesByCmd[cmd]
84
            except KeyError:
85
                raise InjectionError(f"Search command {cmd} (key {key}) does not exist")
86
            # use defaults
87
            params = dict(self.meta)
88
            # they shouldn't pass any of these args
89
            if "path" in e:
90
                raise ValueError(f"Cannot set 'path' in [[search]]")
0 ignored issues
show
introduced by
Using an f-string that does not have any interpolated variables
Loading history...
91
            for bad in {*meta_keys, "no_setup"}:
92
                if bad in e:
93
                    raise ValueError(f"Cannot set '{bad}' in [[search]]; set in meta")
94
            # update the defaults from 'meta' (e.g. 'verbose')
95
            # skip the source -- it's the command name
96
            # stupidly, we need to explicitly add the defaults from the OptionInfo instances
97
            params.update(cmd.default_param_values().items())
98
            # do this after: the defaults had path, key, and to
99
            # params["path"] = self.path
100
            params["key"] = key
101
            params["to"] = self.format
102
            params.update({k: v for k, v in e.items() if k != "source"})
103
            del params["check"]
104
            cmd.test(self.path, **{**params, **dict(quiet=True)})
105
            if key in commands:
106
                raise ValueError(f"Repeated search key '{key}'")
107
            commands[key] = (cmd, params)
108
        if self.meta.get("check", False):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
109
            # we already ran check on all of them
110
            return {}
111
        else:
112
            return commands
113
114
115
__all__ = ["MultiSearch"]
116