Passed
Push — main ( 7b3fbc...cf9f8c )
by Douglas
01:44
created

mandos.entries.common_args   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 180
dl 0
loc 341
rs 10
c 0
b 0
f 0
wmc 29

20 Methods

Rating   Name   Duplication   Size   Complexity  
A Arg.out_file() 0 4 1
A Opt.in_dir() 0 4 1
A Arg.out_path() 0 4 1
A Opt.in_file() 0 4 1
A _Args._path() 0 18 3
A Opt.flag() 0 3 1
A Arg.in_dir() 0 4 1
A Arg.out_dir() 0 4 1
A Arg.x() 0 3 1
A Opt.in_path() 0 12 1
A Opt.out_file() 0 4 1
A Opt.out_dir() 0 4 1
A Opt.val() 0 3 1
A Arg.in_file() 0 4 1
A Arg.in_path() 0 4 1
A _Args._arg() 0 8 2
A Opt.out_path() 0 12 1
A CommonArgs.parse_taxon_id() 0 6 2
A CommonArgs.parse_taxon_id_or_name() 0 7 4
A CommonArgs.parse_taxa() 0 4 1

2 Functions

Rating   Name   Duplication   Size   Complexity  
A run() 0 6 1
A _strip() 0 2 1
1
"""
2
Common argument processing and arguments for Typer.
3
"""
4
5
from inspect import cleandoc
6
from pathlib import Path
7
from typing import Optional, TypeVar, Sequence, Union
8
9
import typer
0 ignored issues
show
introduced by
Unable to import 'typer'
Loading history...
10
from mandos.model.settings import MANDOS_SETTINGS
11
12
T = TypeVar("T", covariant=True)
0 ignored issues
show
Coding Style Naming introduced by
Class name "T" 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...
13
14
15
class _Args:
16
    @staticmethod
17
    def _arg(doc: str, *names, default: Optional[T] = None, req: bool = False, **kwargs):
18
        kwargs = dict(
19
            help=cleandoc(doc),
20
            **kwargs,
21
            allow_dash=True,
22
        )
23
        return typer.Argument(default, **kwargs) if req else typer.Option(default, *names, **kwargs)
24
25
    @staticmethod
26
    def _path(
27
        doc: str, *names, default: Optional[str], f: bool, d: bool, out: bool, req: bool, **kwargs
0 ignored issues
show
Coding Style Naming introduced by
Variable name "f" 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 introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style Naming introduced by
Variable name "d" 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...
28
    ):
29
        # if it's None, we're going to have a special default set afterward, so we'll explain it in the doc
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/100).

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

Loading history...
30
        if out and default is None:
31
            kwargs = dict(show_default=True, **kwargs)
32
        kwargs = {
33
            **dict(
34
                exists=not out,
35
                dir_okay=d,
36
                file_okay=f,
37
                readable=out,
38
                writable=not out,
39
            ),
40
            **kwargs,
41
        }
42
        return _Args._arg(doc, *names, default=default, req=req, **kwargs)
43
44
45
class Arg(_Args):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
46
    @staticmethod
47
    def out_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
48
        return _Args._path(
49
            doc, *names, default=default, f=True, d=False, out=True, req=True, **kwargs
50
        )
51
52
    @staticmethod
53
    def out_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
54
        return _Args._path(
55
            doc, *names, default=default, f=True, d=True, out=True, req=True, **kwargs
56
        )
57
58
    @staticmethod
59
    def out_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
60
        return _Args._path(
61
            doc, *names, default=default, f=True, d=True, out=False, req=True, **kwargs
62
        )
63
64
    @staticmethod
65
    def in_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
66
        return _Args._path(
67
            doc, *names, default=default, f=True, d=False, out=False, req=True, **kwargs
68
        )
69
70
    @staticmethod
71
    def in_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
72
        return _Args._path(
73
            doc, *names, default=default, f=False, d=True, out=False, req=True, **kwargs
74
        )
75
76
    @staticmethod
77
    def in_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
78
        return _Args._path(
79
            doc, *names, default=default, f=True, d=True, out=False, req=True, **kwargs
80
        )
81
82
    @staticmethod
83
    def x(doc: str, *names, default: Optional[T] = None, **kwargs):
0 ignored issues
show
Coding Style Naming introduced by
Method 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...
introduced by
Missing function or method docstring
Loading history...
84
        return _Args._arg(doc, *names, default=default, req=True, **kwargs)
85
86
87
class Opt(_Args):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
88
    @staticmethod
89
    def out_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
90
        return _Args._path(
91
            doc, *names, default=default, f=True, d=False, out=True, req=False, **kwargs
92
        )
93
94
    @staticmethod
95
    def out_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
96
        return _Args._path(
97
            doc, *names, default=default, f=True, d=True, out=True, req=False, **kwargs
98
        )
99
100
    @staticmethod
101
    def out_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
102
        return _Args._path(
103
            doc,
104
            *names,
105
            default=default,
106
            f=True,
107
            d=True,
108
            out=False,
109
            req=False,
110
            exists=False,
111
            **kwargs,
112
        )
113
114
    @staticmethod
115
    def in_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
116
        return _Args._path(
117
            doc, *names, default=default, f=True, d=False, out=False, req=False, **kwargs
118
        )
119
120
    @staticmethod
121
    def in_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
122
        return _Args._path(
123
            doc, *names, default=default, f=False, d=True, out=False, req=False, **kwargs
124
        )
125
126
    @staticmethod
127
    def in_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
128
        return _Args._path(
129
            doc,
130
            *names,
131
            default=default,
132
            f=True,
133
            d=True,
134
            out=False,
135
            req=False,
136
            exists=False,
137
            **kwargs,
138
        )
139
140
    @staticmethod
141
    def val(doc: str, *names, default: Optional[T] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
142
        return _Args._arg(doc, *names, default=default, req=False, **kwargs)
143
144
    @staticmethod
145
    def flag(doc: str, *names, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
146
        return _Args._arg(doc, *names, default=False, req=False, **kwargs)
147
148
149
def _strip(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...
150
    return s.strip().strip("'").strip('"').strip()
151
152
153
class CommonArgs:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
154
    @staticmethod
155
    def parse_taxon_id_or_name(taxon: Union[int, str]) -> Union[int, str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
156
        if isinstance(taxon, str):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
157
            return taxon
158
        elif isinstance(taxon, str) and taxon.isdigit():
159
            return int(taxon)
160
        raise ValueError(f"Taxon {taxon} must be an ID or name")
161
162
    @staticmethod
163
    def parse_taxon_id(taxon: Union[int, str]) -> int:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
164
        try:
165
            return int(taxon)
166
        except ValueError:
167
            raise ValueError(f"Taxon {taxon} must be an exact ID") from None
168
169
    @staticmethod
170
    def parse_taxa(taxa: str) -> Sequence[Union[int, str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
171
        taxa = [_strip(t) for t in taxa.split(",")]
172
        return [CommonArgs.parse_taxon_id_or_name(t) for t in taxa]
173
174
    output_formats = r"""
175
        The filename extension must be one of: .feather; .snappy/.parquet;
176
        .csv, .tsv, .tab, .json (with optional .gz/.bz2/.zip/.xz);
177
        Feather (.feather) and Parquet (.snappy) are recommended.
178
        If only a filename suffix is provided, only sets the format and filename suffix.
179
        If no extension is provided, interprets that path as a directory and uses the default format (Feather).
180
181
        Will fail if the file exists, unless `--replace` is passed.
182
    """
183
184
    input_formats = r"""
185
        The filename extension must be one of: .feather; .snappy/.parquet;
186
        .csv, .tsv, .tab (with optional .gz/.bz2/.zip/.xz);
187
        Feather (.feather) and Parquet (.snappy) are recommended formats.
188
        (Some other formats, such as .json or .h5, may be permitted but are discouraged.)
189
    """
190
191
    file_input = Arg.in_file("The path to a file output by `:concat` or `:search`.", "input")
192
193
    compounds = Arg.in_file(
194
        """
195
        The path to the input file.
196
        One of:
197
198
          (A) *.txt, *.lines, or *.list (optionally with .gz/.zip/.xz/.bz2)), with one InChI Key per line;
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

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

Loading history...
199
200
          (B) A *.csv, *.tsv, *.tab file (or .gz/.zip/.xz/.bz2 variant) with a column called 'inchikey'; OR
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/100).

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

Loading history...
201
202
          (C) An Arrow *.feather file or Parquet *.snappy file with a column called 'inchikey'
203
        """
204
    )
205
206
    dir_input = Arg.in_dir(
207
        rf"""
208
        The path to a directory containing files output from mandos search.
209
210
        {input_formats}
211
        Note that *all* matching files will be included.
212
        Provide ``--exclude`` if needed.
213
        """
214
    )
215
216
    to_single = Opt.out_file(
217
        rf"""
218
        The path to the output file.
219
220
        {output_formats}
221
222
        [default: <input-path>/{...}.{MANDOS_SETTINGS.default_table_suffix}.gz]
223
        """,
224
        "--to",
225
    )
226
227
    input_matrix: Path = Arg.in_file(
228
        rf"""
229
        The path to a similarity matrix file to write to.
230
231
        {input_formats}
232
        .txt/.txt.gz/etc. is assumed to be whitespace-delimited.
233
        TCompounds can be referenced by InChI Key or compound ID (matching what you provided for the search).
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (109/100).

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

Loading history...
234
        The set of compounds here must exactly match the set of compounds in the input files.
235
        For Excel and text formats, the first row and the first column (header and index) indicate the compounds.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

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

Loading history...
236
237
        Values must be floating-point.
238
        """
239
    )
240
    replace: bool = typer.Option(
241
        False, help="Replace output file(s) if they exist. See also: --skip"
242
    )
243
244
    taxa = Opt.val(
245
        r"""
246
        The IDs or names of UniProt taxa, comma-separated.
247
        Taxon names and common names can be used for vertebrate species (where available).
248
249
        This can have a significant effect on searches. See the docs for more info.
250
251
        [default: 7742] (Euteleostomi)
252
        """,
253
        "--taxa",
254
        "7742",
255
        show_default=False,
256
    )
257
258
    seed = Opt.val(r"A random seed (integer).", "--seed", default=0)
259
260
    n_samples = Opt.val(
261
        "Number of bootstrap samples (positive integer).",
262
        "--samples",
263
        min=1,
264
        default=2000,
265
    )
266
267
    exclude = Opt.val("A glob pattern matching input filenames to ignore.")
268
269
    verbose: bool = Opt.flag(
270
        r"Configure logger to output INFO (use ``--quiet`` for less info)",
271
        "-v",
272
        "--verbose",
273
    )
274
275
    quiet: bool = Opt.flag(
276
        r"Configure logger to output only ERROR (use ``--verbose`` for more info)",
277
        "-q",
278
        "--quiet",
279
    )
280
281
    in_cache: bool = Opt.flag(
282
        r"Do not download any data and fail if needed data is not cached.",
283
        hidden=True,
284
    )
285
286
    as_of: Optional[str] = Opt.val(
287
        f"""
288
        Restrict to data that was cached as of some date and time.
289
        This option can be useful for reproducibility.
290
291
        Note that this should imply that underlying data sources (such as of deposition or publication)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

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

Loading history...
292
        are restricted by this datetime, but that is not checked.
293
294
        Examples:
295
296
            - --as-of 2021-10-11T14:12:13Z
297
            - --as-of 2021-10-11T14:12:13+14:00
298
            - --as-of 2021-10-11T14:12:13.496Z
299
            - --as-of "2021-10-11 14:12:13,496,915+14:00"
300
            - --as-of "2021-10-11 14:12:13-8:00 [America/Los_Angeles]"
301
302
        This is a subset of ISO 8601, represented as ``YYYY-mm-dd('T'|' '):hh:MM:ss(i)Z``.
303
        Precision must be nanosecond or less, and ``,`` and ``.`` are equivalent as a thousands separator.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

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

Loading history...
304
        You can provide an IANA zone name in square brackets for context, but the offset is still required.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/100).

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

Loading history...
305
        """
0 ignored issues
show
introduced by
Using an f-string that does not have any interpolated variables
Loading history...
306
    )
307
308
    log_path = Opt.out_path(
309
        r"""
310
        Also log to a file.
311
        The suffix can be .log, .log.gz, .log.zip, or .json, .json.gz, or .json.gz.
312
        You can prefix the path with :LEVEL: to control the level. For example, ``:INFO:out.log``
313
        """,
314
        "--log",
315
        show_default=True,
316
    )
317
318
    no_setup: bool = Opt.flag(
319
        r"Skip setup, such as configuring logging.",
320
        "--no-setup",
321
        hidden=True,
322
    )
323
324
325
cli = typer.Typer()
326
327
328
@cli.command()
329
def run(
0 ignored issues
show
Coding Style Naming introduced by
Argument 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...
introduced by
Missing function or method docstring
Loading history...
330
    path: Path = CommonArgs.dir_input,
0 ignored issues
show
Unused Code introduced by
The argument path seems to be unused.
Loading history...
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
331
    x=CommonArgs.log_path,
0 ignored issues
show
Unused Code introduced by
The argument x seems to be unused.
Loading history...
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
332
):
333
    pass
334
335
336
if __name__ == "__main__":
337
    typer.run(run)
338
339
340
__all__ = ["CommonArgs", "Arg", "Opt"]
341