Passed
Push — dependabot/pip/sphinx-rtd-them... ( 5d7d96...5d0a9b )
by
unknown
01:56
created

EntryUtils.adjust_dir_name()   D

Complexity

Conditions 12

Size

Total Lines 31
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 30
nop 5
dl 0
loc 31
rs 4.8
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like mandos.entry._arg_utils.EntryUtils.adjust_dir_name() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
from __future__ import annotations
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import os
3
from dataclasses import dataclass
4
from inspect import cleandoc
5
from pathlib import Path
6
from typing import (
7
    AbstractSet,
8
    Any,
9
    Callable,
10
    Iterable,
11
    Mapping,
12
    Optional,
13
    Sequence,
14
    Set,
15
    Tuple,
16
    TypeVar,
17
    Union,
18
)
19
20
import typer
0 ignored issues
show
introduced by
Unable to import 'typer'
Loading history...
21
from pocketutils.core.exceptions import PathExistsError, XTypeError, XValueError
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.exceptions'
Loading history...
22
from pocketutils.tools.path_tools import PathTools
0 ignored issues
show
introduced by
Unable to import 'pocketutils.tools.path_tools'
Loading history...
23
from regex import regex
0 ignored issues
show
introduced by
Unable to import 'regex'
Loading history...
24
from typeddfs.df_errors import FilenameSuffixError
0 ignored issues
show
introduced by
Unable to import 'typeddfs.df_errors'
Loading history...
25
26
from mandos.model.apis.chembl_support.chembl_targets import TargetType
27
from mandos.model.apis.pubchem_support.pubchem_models import ClinicalTrialsGovUtils
28
from mandos.model.settings import SETTINGS, Globals
29
from mandos.model.taxonomy import Taxonomy
30
from mandos.model.taxonomy_caches import TaxonomyFactories
31
from mandos.model.utils.setup import logger
32
33
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...
34
35
36
@dataclass(frozen=True, repr=True, order=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
37
class ParsedTaxa:
38
    source: str
39
    allow: Sequence[Union[int, str]]
40
    forbid: Sequence[Union[int, str]]
41
    ancestors: Sequence[Union[int, str]]
42
43
    @classmethod
44
    def empty(cls) -> ParsedTaxa:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
45
        return ParsedTaxa("", [], [], [])
46
47
48
class _Args:
49
    @staticmethod
50
    def _arg(doc: str, *names, default: Optional[T] = None, req: bool = False, **kwargs):
51
        kwargs = dict(
52
            help=cleandoc(doc),
53
            **kwargs,
54
            allow_dash=True,
55
        )
56
        if req:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
57
            return typer.Argument(default, **kwargs)
58
        else:
59
            return typer.Option(default, *names, **kwargs)
60
61
    @staticmethod
62
    def _path(
63
        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 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...
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
64
    ):
65
        # 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...
66
        if out and default is None:
67
            kwargs = dict(show_default=False, **kwargs)
68
        kwargs = {
69
            **dict(
70
                exists=not out,
71
                dir_okay=d,
72
                file_okay=f,
73
                readable=out,
74
                writable=not out,
75
            ),
76
            **kwargs,
77
        }
78
        return _Args._arg(doc, *names, default=default, req=req, **kwargs)
79
80
81
class Arg(_Args):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
82
    @staticmethod
83
    def out_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
84
        return _Args._path(
85
            doc, *names, default=default, f=True, d=False, out=True, req=True, **kwargs
86
        )
87
88
    @staticmethod
89
    def out_dir(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=True, out=True, req=True, **kwargs
92
        )
93
94
    @staticmethod
95
    def out_path(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=False, req=True, **kwargs
98
        )
99
100
    @staticmethod
101
    def in_file(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, *names, default=default, f=True, d=False, out=False, req=True, **kwargs
104
        )
105
106
    @staticmethod
107
    def in_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
108
        return _Args._path(
109
            doc, *names, default=default, f=False, d=True, out=False, req=True, **kwargs
110
        )
111
112
    @staticmethod
113
    def in_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
114
        return _Args._path(
115
            doc, *names, default=default, f=True, d=True, out=False, req=True, **kwargs
116
        )
117
118
    @staticmethod
119
    def val(doc: str, *names, default: Optional[T] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
120
        return _Args._arg(doc, *names, default=default, req=True, **kwargs)
121
122
123
class Opt(_Args):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
124
    @staticmethod
125
    def out_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
126
        return _Args._path(
127
            doc, *names, default=default, f=True, d=False, out=True, req=False, **kwargs
128
        )
129
130
    @staticmethod
131
    def out_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
132
        return _Args._path(
133
            doc, *names, default=default, f=True, d=True, out=True, req=False, **kwargs
134
        )
135
136
    @staticmethod
137
    def out_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
138
        return _Args._path(
139
            doc,
140
            *names,
141
            default=default,
142
            f=True,
143
            d=True,
144
            out=False,
145
            req=False,
146
            exists=False,
147
            **kwargs,
148
        )
149
150
    @staticmethod
151
    def in_file(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
152
        return _Args._path(
153
            doc, *names, default=default, f=True, d=False, out=False, req=False, **kwargs
154
        )
155
156
    @staticmethod
157
    def in_dir(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
158
        return _Args._path(
159
            doc, *names, default=default, f=False, d=True, out=False, req=False, **kwargs
160
        )
161
162
    @staticmethod
163
    def in_path(doc: str, *names, default: Optional[str] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
164
        return _Args._path(
165
            doc,
166
            *names,
167
            default=default,
168
            f=True,
169
            d=True,
170
            out=False,
171
            req=False,
172
            exists=False,
173
            **kwargs,
174
        )
175
176
    @staticmethod
177
    def val(doc: str, *names, default: Optional[T] = None, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
178
        return _Args._arg(doc, *names, default=default, req=False, **kwargs)
179
180
    @staticmethod
181
    def flag(doc: str, *names, **kwargs):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
182
        return _Args._arg(doc, *names, default=False, req=False, **kwargs)
183
184
185
class ArgUtils:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
186
    @classmethod
187
    def definition_bullets(cls, dct: Mapping[Any, Any], colon: str = ": ", indent: int = 12) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
188
        joiner = os.linesep * 2 + " " * indent
189
        jesus = [f" - {k}{colon}{v}" for k, v in dct.items()]
190
        return joiner.join(jesus)
191
192
    @classmethod
193
    def definition_list(cls, dct: Mapping[Any, Any], colon: str = ": ", sep: str = "; ") -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
194
        jesus = [f"{k}{colon}{v}" for k, v in dct.items()]
195
        return sep.join(jesus)
196
197
    @classmethod
198
    def list(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
199
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
200
        lst: Iterable[Any],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
201
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
202
        attr: Union[None, str, Callable[[Any], Any]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
203
        sep: str = ", ",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
204
    ) -> str:
205
        x = []
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...
206
        for v in lst:
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...
207
            if attr is None and hasattr(v, "name"):
208
                x += [v.name]
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...
209
            elif attr is None:
210
                x += [str(v)]
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...
211
            elif isinstance(attr, str):
212
                x += [str(getattr(v, attr))]
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...
213
            else:
214
                x += [str(attr(v))]
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...
215
        return sep.join(x)
216
217
    @classmethod
218
    def get_taxonomy(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
219
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
220
        taxa: Optional[str],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
221
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
222
        local_only: bool = False,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
223
        allow_forbid: bool = True,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
224
    ) -> Optional[Taxonomy]:
225
        if taxa is None or len(taxa) == 0:
226
            return None
227
        parsed = cls.parse_taxa(taxa, allow_forbid=allow_forbid)
228
        return TaxonomyFactories.get_smart_taxonomy(
229
            allow=parsed.allow,
230
            forbid=parsed.forbid,
231
            ancestors=parsed.ancestors,
232
            local_only=local_only,
233
        )
234
235
    @classmethod
236
    def parse_taxa(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
237
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
238
        taxa: Optional[str],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
239
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
240
        allow_forbid: bool = True,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
241
    ) -> ParsedTaxa:
242
        if taxa is None or taxa == "":
243
            return ParsedTaxa.empty()
244
        ancestors = f"{Globals.cellular_taxon},{Globals.viral_taxon}"
245
        if ":" in taxa:
246
            ancestors = taxa.split(":", 1)[1]
247
            taxa = taxa.split(":", 1)[0]
248
        taxa_objs = [t.strip() for t in taxa.split(",") if len(t.strip()) > 0]
249
        allow = [t.strip().lstrip("+") for t in taxa_objs if not t.startswith("-")]
250
        forbid = [t.strip().lstrip("-") for t in taxa_objs if t.startswith("-")]
251
        ancestors = [t.strip() for t in ancestors.split(",")]
252
        if not allow_forbid and len(forbid) > 0:
253
            raise XValueError(f"Cannot use '-' in {taxa}")
254
        return ParsedTaxa(
255
            source=taxa,
256
            allow=[ArgUtils.parse_taxon(t, id_only=False) for t in allow],
257
            forbid=[ArgUtils.parse_taxon(t, id_only=False) for t in forbid],
258
            ancestors=[ArgUtils.parse_taxon(t, id_only=True) for t in ancestors],
259
        )
260
261
    @classmethod
262
    def parse_taxa_ids(cls, taxa: str) -> Sequence[int]:
263
        """
264
        Does not allow negatives.
265
        """
266
        if taxa is None or taxa == "":
267
            return []
268
        taxa = [t.strip() for t in taxa.split(",") if len(t.strip()) > 0]
269
        return [ArgUtils.parse_taxon(t, id_only=True) for t in taxa]
270
271
    @classmethod
272
    def parse_taxon(cls, taxon: Union[int, str], *, id_only: bool = False) -> Union[int, str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
273
        std = cls._get_std_taxon(taxon)
274
        if isinstance(taxon, str) and taxon in std:
275
            return std
276
        if isinstance(taxon, str) and not id_only:
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
277
            return taxon
278
        elif isinstance(taxon, str) and taxon.isdigit():
279
            return int(taxon)
280
        if id_only:
281
            raise XTypeError(f"Taxon {taxon} must be an ID")
282
        raise XTypeError(f"Taxon {taxon} must be an ID or name")
283
284
    @classmethod
285
    def _get_std_taxon(cls, taxa: str) -> str:
286
        x = dict(
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...
287
            vertebrata=Globals.vertebrata,
288
            vertebrate=Globals.vertebrata,
289
            vertebrates=Globals.vertebrata,
290
            cellular=Globals.cellular_taxon,
291
            cell=Globals.cellular_taxon,
292
            cells=Globals.cellular_taxon,
293
            viral=Globals.viral_taxon,
294
            virus=Globals.viral_taxon,
295
            viruses=Globals.viral_taxon,
296
            all=f"{Globals.cellular_taxon},{Globals.viral_taxon}",
297
        ).get(taxa)
298
        return taxa if x is None else str(x)
299
300
    @staticmethod
301
    def get_trial_statuses(st: str) -> Set[str]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "st" 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...
302
        return ClinicalTrialsGovUtils.resolve_statuses(st)
303
304
    @staticmethod
305
    def get_target_types(st: str) -> Set[str]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "st" 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...
306
        return {s.name for s in TargetType.resolve(st)}
307
308
309
class EntryUtils:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
310
    @classmethod
311
    def adjust_filename(
0 ignored issues
show
Coding Style Naming introduced by
Argument name "to" 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...
312
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
313
        to: Optional[Path],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
314
        default: Union[str, Path],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
315
        replace: bool,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
316
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
317
        suffixes: Union[None, AbstractSet[str], Callable[[Union[Path, str]], Any]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
318
    ) -> Path:
319
        if to is None:
320
            path = Path(default)
321
        elif str(to).startswith("."):
322
            path = Path(default).with_suffix(str(to))
323
        elif str(to).startswith("*."):
324
            path = Path(default).with_suffix(str(to)[1:])
325
        elif to.is_dir() or to.suffix == "":
326
            path = to / default
327
        else:
328
            path = Path(to)
329
        path = Path(path)
330
        if os.name == "nt" and SETTINGS.sanitize_paths:
331
            new_path = PathTools.sanitize_path_nodes(path._parts, is_file=True)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _parts 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...
332
            if new_path.resolve() != path.resolve():
333
                logger.warning(f"Sanitized filename {path} → {new_path}")
334
                path = new_path
335
        if (
336
            path.exists()
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
337
            and not path.is_file()
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
338
            and not path.is_socket()
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
339
            and not path.is_char_device()
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
340
        ):
341
            raise PathExistsError(f"Path {path} exists and is not a file")
342
        if path.exists() and not replace:
343
            raise PathExistsError(f"File {path} already exists")
344
        cls._check_suffix(path.suffix, suffixes)
345
        if path.exists() and replace:
346
            logger.info(f"Overwriting existing file {path}")
347
        return path
348
349
    @classmethod
350
    def adjust_dir_name(
0 ignored issues
show
Coding Style Naming introduced by
Argument name "to" 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...
351
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
352
        to: Optional[Path],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
353
        default: Union[str, Path],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
354
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
355
        suffixes: Union[None, AbstractSet[str], Callable[[Union[Path, str]], Any]] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
356
    ) -> Tuple[Path, str]:
357
        out_dir = Path(default)
358
        suffix = SETTINGS.table_suffix
359
        if to is not None:
360
            pat = regex.compile(r"([^\*]*)(?:\*(\..+))", flags=regex.V1)
361
            m: regex.Match = pat.fullmatch(to)
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...
362
            out_dir = default if m.group(1) == "" else m.group(1)
363
            suffix = SETTINGS.table_suffix if m.group(2) == "" else m.group(2)
364
            if out_dir.startswith("."):
365
                logger.warning(f"Writing to {out_dir} — was it meant as a suffix instead?")
366
            out_dir = Path(out_dir)
367
        if os.name == "nt" and SETTINGS.sanitize_paths:
368
            new_dir = PathTools.sanitize_path_nodes(out_dir._parts, is_file=True)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _parts 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...
369
            if new_dir.resolve() != out_dir.resolve():
370
                logger.warning(f"Sanitized directory {out_dir} → {new_dir}")
371
                out_dir = new_dir
372
        if out_dir.exists() and not out_dir.is_dir():
373
            raise PathExistsError(f"Path {out_dir} already exists but and is not a directory")
374
        cls._check_suffix(suffix, suffixes)
375
        if out_dir.exists():
376
            n_files = len(list(out_dir.iterdir()))
377
            if n_files > 0:
378
                logger.debug(f"Directory {out_dir} is non-emtpy")
379
        return out_dir, suffix
380
381
    @classmethod
382
    def _check_suffix(cls, suffix, suffixes):
383
        if suffixes is not None and callable(suffixes):
384
            try:
385
                suffixes(suffix)  # make sure it's ok
386
            except FilenameSuffixError:
387
                raise XValueError(f"Unsupported file format {suffix}")
388
        elif suffixes is not None:
389
            if suffix not in suffixes:
390
                raise XValueError(f"Unsupported file format {suffix}")
391
392
393
__all__ = ["Arg", "Opt", "ArgUtils", "EntryUtils"]
394