Passed
Push — main ( 520e83...b06663 )
by Douglas
01:43
created

pocketutils.misc.resources.Resources.path()   B

Complexity

Conditions 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 8
nop 3
dl 0
loc 15
rs 8.6666
c 0
b 0
f 0
1
import logging
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
from datetime import datetime, timedelta
0 ignored issues
show
Unused Code introduced by
Unused timedelta imported from datetime
Loading history...
3
from pathlib import Path
4
from typing import AbstractSet, MutableMapping, Optional, Tuple, Union, Any
0 ignored issues
show
Unused Code introduced by
Unused Tuple imported from typing
Loading history...
Unused Code introduced by
Unused Union imported from typing
Loading history...
5
6
import orjson
0 ignored issues
show
introduced by
Unable to import 'orjson'
Loading history...
7
8
from pocketutils.core import PathLike
0 ignored issues
show
introduced by
Cannot import 'pocketutils.core' due to syntax error 'invalid syntax (<unknown>, line 134)'
Loading history...
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
9
from pocketutils.core._internal import read_txt_or_gz, JSON_SUFFIXES, TOML_SUFFIXES, GZ_BZ2_SUFFIXES
0 ignored issues
show
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
Unused Code introduced by
Unused JSON_SUFFIXES imported from pocketutils.core._internal
Loading history...
Unused Code introduced by
Unused TOML_SUFFIXES imported from pocketutils.core._internal
Loading history...
10
from pocketutils.core.chars import Chars
0 ignored issues
show
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
Unused Code introduced by
Unused Chars imported from pocketutils.core.chars
Loading history...
11
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
12
from pocketutils.core.exceptions import (
0 ignored issues
show
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
13
    DirDoesNotExistError,
14
    FileDoesNotExistError,
15
    MissingResourceError,
16
    PathExistsError,
17
)
18
from pocketutils.tools.common_tools import CommonTools
19
from pocketutils.tools.filesys_tools import FilesysTools
20
from pocketutils.tools.unit_tools import UnitTools
0 ignored issues
show
Unused Code introduced by
Unused UnitTools imported from pocketutils.tools.unit_tools
Loading history...
21
22
_logger = logging.getLogger("pocketutils")
23
24
25
class Resources:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
26
    def __init__(self, path: PathLike, *, logger=_logger):
27
        self._dir = Path(path)
28
        self._logger = logger
29
        self._in_memory: MutableMapping[str, Any] = {}
30
31
    @property
32
    def home(self) -> Path:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
33
        return self._dir
34
35
    def to_memory(self, key: str, data: Any):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
36
        self._in_memory[key] = data
37
38
    def from_memory(self, key: str) -> Any:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
39
        return self._in_memory[key]
40
41
    def contains(self, *nodes: PathLike) -> bool:
42
        """Returns whether a resource file (or dir) exists."""
43
        return self.path(*nodes).exists()
44
45
    def contains_a_file(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
46
        self, *nodes: PathLike, suffixes: Optional[AbstractSet[str]] = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
47
    ) -> bool:
48
        try:
49
            self.a_file(*nodes, suffixes=suffixes)
50
            return True
51
        except MissingResourceError:
52
            return False
53
54
    def path(self, *nodes: PathLike, exists: bool = False) -> Path:
55
        """
56
        Gets a path of a test resource file under ``resources/``.
57
58
        Raises:
59
            MissingResourceError: If the path is not found
60
        """
61
        if len(nodes) == 1 and isinstance(nodes[0], Path) and nodes[0].is_absolute():
62
            path = nodes[0]
63
        else:
64
            path = Path(self._dir, *nodes)
65
            path = path.with_suffix(path.suffix)
66
        if exists and not path.exists():
67
            raise MissingResourceError(f"Resource {path} missing")
68
        return path
69
70
    def file(self, *nodes: PathLike) -> Path:
71
        """
72
        Gets a path of a test resource file under ``resources/``.
73
74
        Raises:
75
            MissingResourceError: If the path is not found
76
            PathExistsError: If the path is not a file or symlink to a file or is not readable
77
        """
78
        path = self.path(*nodes)
79
        info = FilesysTools.get_info(path)
80
        if not info.is_file:
81
            raise PathExistsError(f"Resource {path} is not a file!")
82
        if not info.is_readable_file:
83
            raise FileDoesNotExistError(f"Resource {path} is not readable")
84
        return path
85
86
    def dir(self, *nodes: PathLike) -> Path:
87
        """
88
        Gets a path of a test resource file under ``resources/``.
89
90
        Raises:
91
            MissingResourceError: If the path is not found and ``not missing_ok``
92
            PathExistsError: If the path is not a dir, symlink to a dir, or mount,
93
                             or lacks 'R' or 'X' permissions
94
        """
95
        path = self.path(*nodes)
96
        info = FilesysTools.get_info(path)
97
        if not info.exists:
98
            raise DirDoesNotExistError(f"Resource {path} does not exist")
99
        if not info.is_dir:
100
            raise PathExistsError(f"Resource {path} is not a directory")
101
        if info.is_readable_dir:
102
            raise FileDoesNotExistError(f"Resource {path} is not readable")
103
        return path
104
105
    def a_file(self, *nodes: PathLike, suffixes: Optional[AbstractSet[str]] = None) -> Path:
106
        """
107
        Gets a path of a test resource file under ``resources/``, ignoring suffix.
108
109
        Args:
110
            nodes: Path nodes under ``resources/``
111
            suffixes: Set of acceptable suffixes; if None, only the exact file is accepted
112
        """
113
        path = self.path(*nodes)
114
        if path.is_file():
115
            return path
116
        options = [
117
            p
118
            for p in path.parent.glob(path.stem + "*")
119
            if p.is_file() and (suffixes is None or p.suffix in suffixes)
120
        ]
121
        try:
122
            return CommonTools.only(options)
123
        except LookupError:
124
            raise MissingResourceError(f"Resource {path} missing") from None
125
126
    def toml(self, *nodes: PathLike) -> NestedDotDict:
127
        """Reads a TOML file under ``resources/``."""
128
        path = self.a_file(*nodes, suffixes=GZ_BZ2_SUFFIXES)
129
        return NestedDotDict.read_toml(path)
130
131
    def json(self, *nodes: PathLike) -> NestedDotDict:
132
        """Reads a JSON file under ``resources/``."""
133
        path = self.a_file(*nodes, suffixes=GZ_BZ2_SUFFIXES)
134
        return NestedDotDict.read_json(path)
135
136
    def json_dict(self, *nodes: PathLike) -> MutableMapping:
137
        """Reads a JSON file under ``resources/``."""
138
        path = self.a_file(*nodes, suffixes=GZ_BZ2_SUFFIXES)
139
        return orjson.loads(read_txt_or_gz(path))
140
141
142
__all__ = ["Resources"]
143