1
"""Utility functions."""
9
from collections import OrderedDict
13
__all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8',
14
'to_json', 'from_json', 'matches_requirement']
16
def urlsafe_b64encode(data):
17
"""urlsafe_b64encode without padding"""
18
return base64.urlsafe_b64encode(data).rstrip(binary('='))
21
def urlsafe_b64decode(data):
22
"""urlsafe_b64decode without padding"""
23
pad = b'=' * (4 - (len(data) & 3))
24
return base64.urlsafe_b64decode(data + pad)
28
'''Convert given data to JSON.'''
29
return json.dumps(o, sort_keys=True)
33
'''Decode a JSON payload.'''
36
def open_for_csv(name, mode):
37
if sys.version_info[0] < 3:
41
nl = { 'newline': '' }
43
return open(name, mode + bin, **nl)
49
'''Utf-8 encode data.'''
50
if isinstance(data, unicode):
51
return data.encode('utf-8')
55
'''Utf-8 encode data.'''
56
if isinstance(data, str):
57
return data.encode('utf-8')
62
# For encoding ascii back and forth between bytestrings, as is repeatedly
63
# necessary in JSON-based crypto under Python 3
68
if isinstance(s, unicode):
69
return s.encode('ascii')
73
if isinstance(s, bytes):
74
return s.decode('ascii')
77
if isinstance(s, str):
78
return s.encode('ascii')
80
class HashingFile(object):
81
def __init__(self, fd, hashtype='sha256'):
83
self.hashtype = hashtype
84
self.hash = hashlib.new(hashtype)
86
def write(self, data):
87
self.hash.update(data)
88
self.length += len(data)
93
if self.hashtype == 'md5':
94
return self.hash.hexdigest()
95
digest = self.hash.digest()
96
return self.hashtype + '=' + native(urlsafe_b64encode(digest))
98
class OrderedDefaultDict(OrderedDict):
99
def __init__(self, *args, **kwargs):
101
self.default_factory = None
103
if not (args[0] is None or callable(args[0])):
104
raise TypeError('first argument must be callable or None')
105
self.default_factory = args[0]
107
super(OrderedDefaultDict, self).__init__(*args, **kwargs)
109
def __missing__ (self, key):
110
if self.default_factory is None:
112
self[key] = default = self.default_factory()
115
if sys.platform == 'win32':
116
import ctypes.wintypes
117
# CSIDL_APPDATA for reference - not used here for compatibility with
118
# dirspec, which uses LOCAL_APPDATA and COMMON_APPDATA in that order
119
csidl = dict(CSIDL_APPDATA=26, CSIDL_LOCAL_APPDATA=28,
120
CSIDL_COMMON_APPDATA=35)
122
SHGFP_TYPE_CURRENT = 0
123
buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
124
ctypes.windll.shell32.SHGetFolderPathW(0, csidl[name], 0, SHGFP_TYPE_CURRENT, buf)
127
def save_config_path(*resource):
128
appdata = get_path("CSIDL_LOCAL_APPDATA")
129
path = os.path.join(appdata, *resource)
130
if not os.path.isdir(path):
133
def load_config_paths(*resource):
134
ids = ["CSIDL_LOCAL_APPDATA", "CSIDL_COMMON_APPDATA"]
137
path = os.path.join(base, *resource)
138
if os.path.exists(path):
141
def save_config_path(*resource):
142
import xdg.BaseDirectory
143
return xdg.BaseDirectory.save_config_path(*resource)
144
def load_config_paths(*resource):
145
import xdg.BaseDirectory
146
return xdg.BaseDirectory.load_config_paths(*resource)
148
def matches_requirement(req, wheels):
149
"""List of wheels matching a requirement.
151
:param req: The requirement to satisfy
152
:param wheels: List of wheels to search.
155
from pkg_resources import Distribution, Requirement
157
raise RuntimeError("Cannot use requirements without pkg_resources")
159
req = Requirement.parse(req)
163
f = wf.parsed_filename
164
dist = Distribution(project_name=f.group("name"), version=f.group("ver"))