/
utils.py
144 lines (116 loc) · 3.62 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
"""A module to handle generic operations.
"""
from __future__ import absolute_import
try: #PY3
import collections.abc
except ImportError: #PY2: The relevant classes used to be somewhere else
class collections:
import collections as abc
import mimetypes
import os
import six
from functools import wraps
path_str_types = (six.text_type, six.binary_type)
path_obj_types = ()
if hasattr(os, "PathLike"): #PY36+
path_obj_types += (os.PathLike,)
def convert_path(path):
# Not needed since all system APIs also accept an `os.PathLike`
return path
else: #PY35
try: #PY2: doesn't have `pathlib`
import pathlib
path_obj_types += (pathlib.PurePath,)
except ImportError:
pass
# Independently maintained forward-port of `pathlib` for Py27 and others
try:
import pathlib2
path_obj_types += (pathlib2.PurePath,)
except ImportError:
pass
def convert_path(path):
# `pathlib`'s PathLike objects need to be treated specially and
# converted to strings when interacting with system APIs
return str(path) if isinstance(path, path_obj_types) else path
path_types = path_str_types + path_obj_types
def guess_mimetype(filename):
"""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 : str
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'
def clean_file(file):
"""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 : Union[str, bytes, os.PathLike, io.IOBase, int]
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'):
return open(convert_path(file), 'rb'), True
else:
return file, False
def clean_files(files):
"""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 : Union[str, bytes, os.PathLike, io.IOBase, int, collections.abc.Iterable]
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 files:
yield clean_file(f)
else:
yield clean_file(files)
class return_field(object):
"""Decorator that returns the given field of a json response.
Parameters
----------
field : object
The response field to be returned for all invocations
"""
def __init__(self, field):
self.field = field
def __call__(self, cmd):
"""Wraps a command so that only a specified field is returned.
Parameters
----------
cmd : callable
A command that is intended to be wrapped
"""
@wraps(cmd)
def wrapper(*args, **kwargs):
"""Returns the specified field of the command invocation.
Parameters
----------
args : list
Positional parameters to pass to the wrapped callable
kwargs : dict
Named parameter to pass to the wrapped callable
"""
res = cmd(*args, **kwargs)
return res[self.field]
return wrapper