Passed
Push — main ( cdf0f7...3de8e8 )
by Douglas
01:40
created

MultiSearch.__init__()   B

Complexity

Conditions 7

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 19
nop 3
dl 0
loc 23
rs 8
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, Union, Type
10
import typer
0 ignored issues
show
introduced by
Unable to import 'typer'
Loading history...
11
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.dot_dict'
Loading history...
12
13
from mandos import logger
0 ignored issues
show
Unused Code introduced by
Unused logger imported from mandos
Loading history...
14
from mandos.entries.paths import EntryPaths
0 ignored issues
show
Unused Code introduced by
Unused EntryPaths imported from mandos.entries.paths
Loading history...
15
from mandos.model import InjectionError
16
from mandos.entries.entries import Entries, Entry
17
from mandos.entries.api_singletons import Apis
18
19
cli = typer.Typer()
20
Apis.set_default()
21
Chembl, Pubchem = Apis.Chembl, Apis.Pubchem
22
23
EntriesByCmd: typing.Dict[str, Type[Entry]] = {e.cmd(): e for e in Entries}
24
25
# these are only permitted in 'meta', not individual searches
26
meta_keys = {"verbose", "quiet", "check", "log", "to"}
27
28
29
class MultiSearch:
30
    """
31
    Ugh.
32
    """
33
34
    def __init__(self, path: Path, config: Optional[Path]):
35
        if config is None:
36
            config = path.with_suffix(".toml")
37
        self.path = path
38
        if not self.path.exists():
39
            raise FileNotFoundError(f"File {path} not found")
40
        if not config.exists():
41
            raise FileNotFoundError(f"File {config} not found")
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, ".feather")
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
        for key, (ent, params) in cmds.items():
0 ignored issues
show
Unused Code introduced by
The variable key seems to be unused.
Loading history...
62
            ent.run(self.path, no_setup=True, **params)
63
64
    def _build_commands(
65
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
66
    ) -> typing.Dict[str, typing.Tuple[Type[Entry], typing.Mapping[str, Union[int, str, float]]]]:
67
        commands: typing.Dict[
68
            str, typing.Tuple[Type[Entry], typing.Mapping[str, Union[int, str, float]]]
69
        ] = {}
70
        # build up the lit of Entry classes first, and run ``test`` on each one
71
        # that's to check that the parameters are correct before running anything
72
        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...
73
            cmd = e.req_as("source", str)
74
            key = e.req_as("key", str)
75
            # use defaults
76
            params = dict(self.meta)
77
            # they shouldn't pass any of these args
78
            if "path" in e:
79
                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...
80
            for bad in {*meta_keys, "no_setup"}:
81
                if bad in e:
82
                    raise ValueError(f"Cannot set '{bad}' in [[search]]; set in meta")
83
            # update the defaults from 'meta' (e.g. 'verbose')
84
            # skip the source -- it's the command name
85
            params.update({k: v for k, v in e.items() if k != "source"})
86
            try:
87
                cmd = EntriesByCmd[cmd]
88
            except KeyError:
89
                raise InjectionError(f"Search command {cmd} (key {key}) does not exist")
90
            cmd.test(self.path, **params)
91
            if key in commands:
92
                raise ValueError(f"Repeated search key '{key}'")
93
            commands[key] = (cmd, params)
94
        if self.meta.get("check", False):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
95
            # we already ran check on all of them
96
            return {}
97
        else:
98
            return commands
99
100
101
__all__ = ["MultiSearch"]
102