forked from ipfs-shipyard/py-ipfs-http-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
197 lines (149 loc) · 4.64 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
"""A module to handle generic operations.
"""
import mimetypes
import os
import sys
import typing as ty
from functools import wraps
if ty.TYPE_CHECKING:
import typing_extensions as ty_ext
else:
ty_ext = ty
AnyStr = ty.TypeVar('AnyStr', bytes, str)
T = ty.TypeVar("T")
if sys.version_info >= (3, 8): #PY38+
Literal = ty_ext.Literal
Protocol = ty_ext.Protocol
Literal_True = ty.Literal[True]
Literal_False = ty.Literal[False]
else: #PY37-
class Literal(ty.Generic[T]):
...
class Protocol:
...
Literal_True = Literal_False = bool
# `os.PathLike` only has a type param while type checking
if ty.TYPE_CHECKING:
PathLike = os.PathLike
PathLike_str = os.PathLike[str]
PathLike_bytes = os.PathLike[bytes]
else:
class PathLike(Protocol, ty.Generic[AnyStr]):
def __fspath__(self) -> AnyStr:
...
PathLike_str = PathLike_bytes = os.PathLike
path_str_t = ty.Union[str, PathLike_str]
path_bytes_t = ty.Union[bytes, PathLike_bytes]
path_t = ty.Union[path_str_t, path_bytes_t]
AnyPath = ty.TypeVar("AnyPath", str, PathLike_str, bytes, PathLike_bytes)
path_types = (str, bytes, os.PathLike,)
path_obj_types = (os.PathLike,)
# work around GH/mypy/mypy#731: no recursive structural types yet
json_primitive_t = ty.Union[bool, float, int, str]
# noqa: N802
class json_list_t(ty.List["json_value_t"]):
pass
# noqa: N802
class json_dict_t(ty.Dict[str, "json_value_t"]):
pass
json_value_t = ty.Union[
json_primitive_t,
json_list_t,
json_dict_t
]
def maybe_fsencode(val: str, ref: AnyStr) -> AnyStr:
"""Encodes the string *val* using the system filesystem encoding if *ref*
is of type :any:`bytes`"""
if isinstance(ref, bytes):
return os.fsencode(val)
else:
return val
def guess_mimetype(filename: str) -> str:
"""Guesses the mimetype of a file based on the given ``filename``.
.. code-block:: python
>>> guess_mimetype('example.txt')
'text/plain'
>>> guess_mimetype('/foo/bar/example')
'application/octet-stream'
Parameters
----------
filename
The file name or path for which the mimetype is to be guessed
"""
fn = os.path.basename(filename)
return mimetypes.guess_type(fn)[0] or 'application/octet-stream'
clean_file_t = ty.Union[path_t, ty.IO[bytes], int]
def clean_file(file: clean_file_t) -> ty.Tuple[ty.IO[bytes], bool]:
"""Returns a tuple containing a file-like object and a close indicator
This ensures the given file is opened and keeps track of files that should
be closed after use (files that were not open prior to this function call).
Raises
------
OSError
Accessing the given file path failed
Parameters
----------
file
A filepath or file-like object that may or may not need to be
opened
"""
if isinstance(file, int):
return os.fdopen(file, 'rb', closefd=False), True
elif not hasattr(file, 'read'):
file = ty.cast(path_t, file) # Cannot be ty.IO[bytes] without `.read()`
return open(file, 'rb'), True
else:
file = ty.cast(ty.IO[bytes], file) # Must be ty.IO[bytes]
return file, False
def clean_files(files: ty.Union[clean_file_t, ty.Iterable[clean_file_t]]) \
-> ty.Generator[ty.Tuple[ty.IO[bytes], bool], ty.Any, ty.Any]:
"""Generates tuples with a file-like object and a close indicator
This is a generator of tuples, where the first element is the file object
and the second element is a boolean which is True if this module opened the
file (and thus should close it).
Raises
------
OSError
Accessing the given file path failed
Parameters
----------
files
Collection or single instance of a filepath and file-like object
"""
if not isinstance(files, path_types) and not hasattr(files, "read"):
for f in ty.cast(ty.Iterable[clean_file_t], files):
yield clean_file(f)
else:
yield clean_file(ty.cast(clean_file_t, files))
F = ty.TypeVar("F", bound=ty.Callable[..., ty.Dict[str, ty.Any]])
class return_field(ty.Generic[T]):
"""Decorator that returns the given field of a json response.
Parameters
----------
field
The response field to be returned for all invocations
"""
__slots__ = ("field",)
field: str
def __init__(self, field: str) -> None:
self.field = field
def __call__(self, cmd: F) -> ty.Callable[..., T]:
"""Wraps a command so that only a specified field is returned.
Parameters
----------
cmd
A command that is intended to be wrapped
"""
@wraps(cmd)
def wrapper(*args: ty.Any, **kwargs: ty.Any) -> T:
"""Returns the specified field as returned by the wrapped function
Parameters
----------
args
Positional parameters to pass to the wrapped callable
kwargs
Named parameter to pass to the wrapped callable
"""
res = cmd(*args, **kwargs) # type: ty.Dict[str, T]
return res[self.field]
return wrapper