You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
4.6 KiB

7 years ago
  1. """Utility functions."""
  2. import base64
  3. import hashlib
  4. import json
  5. import os
  6. import sys
  7. from collections import OrderedDict
  8. __all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8',
  9. 'to_json', 'from_json', 'matches_requirement']
  10. # For encoding ascii back and forth between bytestrings, as is repeatedly
  11. # necessary in JSON-based crypto under Python 3
  12. if sys.version_info[0] < 3:
  13. text_type = unicode # noqa: F821
  14. def native(s):
  15. return s
  16. else:
  17. text_type = str
  18. def native(s):
  19. if isinstance(s, bytes):
  20. return s.decode('ascii')
  21. return s
  22. def urlsafe_b64encode(data):
  23. """urlsafe_b64encode without padding"""
  24. return base64.urlsafe_b64encode(data).rstrip(binary('='))
  25. def urlsafe_b64decode(data):
  26. """urlsafe_b64decode without padding"""
  27. pad = b'=' * (4 - (len(data) & 3))
  28. return base64.urlsafe_b64decode(data + pad)
  29. def to_json(o):
  30. """Convert given data to JSON."""
  31. return json.dumps(o, sort_keys=True)
  32. def from_json(j):
  33. """Decode a JSON payload."""
  34. return json.loads(j)
  35. def open_for_csv(name, mode):
  36. if sys.version_info[0] < 3:
  37. nl = {}
  38. bin = 'b'
  39. else:
  40. nl = {'newline': ''}
  41. bin = ''
  42. return open(name, mode + bin, **nl)
  43. def utf8(data):
  44. """Utf-8 encode data."""
  45. if isinstance(data, text_type):
  46. return data.encode('utf-8')
  47. return data
  48. def binary(s):
  49. if isinstance(s, text_type):
  50. return s.encode('ascii')
  51. return s
  52. class HashingFile(object):
  53. def __init__(self, path, mode, hashtype='sha256'):
  54. self.fd = open(path, mode)
  55. self.hashtype = hashtype
  56. self.hash = hashlib.new(hashtype)
  57. self.length = 0
  58. def write(self, data):
  59. self.hash.update(data)
  60. self.length += len(data)
  61. self.fd.write(data)
  62. def close(self):
  63. self.fd.close()
  64. def digest(self):
  65. if self.hashtype == 'md5':
  66. return self.hash.hexdigest()
  67. digest = self.hash.digest()
  68. return self.hashtype + '=' + native(urlsafe_b64encode(digest))
  69. def __enter__(self):
  70. return self
  71. def __exit__(self, exc_type, exc_val, exc_tb):
  72. self.fd.close()
  73. class OrderedDefaultDict(OrderedDict):
  74. def __init__(self, *args, **kwargs):
  75. if not args:
  76. self.default_factory = None
  77. else:
  78. if not (args[0] is None or callable(args[0])):
  79. raise TypeError('first argument must be callable or None')
  80. self.default_factory = args[0]
  81. args = args[1:]
  82. super(OrderedDefaultDict, self).__init__(*args, **kwargs)
  83. def __missing__(self, key):
  84. if self.default_factory is None:
  85. raise KeyError(key)
  86. self[key] = default = self.default_factory()
  87. return default
  88. if sys.platform == 'win32':
  89. import ctypes.wintypes
  90. # CSIDL_APPDATA for reference - not used here for compatibility with
  91. # dirspec, which uses LOCAL_APPDATA and COMMON_APPDATA in that order
  92. csidl = dict(CSIDL_APPDATA=26, CSIDL_LOCAL_APPDATA=28, CSIDL_COMMON_APPDATA=35)
  93. def get_path(name):
  94. SHGFP_TYPE_CURRENT = 0
  95. buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
  96. ctypes.windll.shell32.SHGetFolderPathW(0, csidl[name], 0, SHGFP_TYPE_CURRENT, buf)
  97. return buf.value
  98. def save_config_path(*resource):
  99. appdata = get_path("CSIDL_LOCAL_APPDATA")
  100. path = os.path.join(appdata, *resource)
  101. if not os.path.isdir(path):
  102. os.makedirs(path)
  103. return path
  104. def load_config_paths(*resource):
  105. ids = ["CSIDL_LOCAL_APPDATA", "CSIDL_COMMON_APPDATA"]
  106. for id in ids:
  107. base = get_path(id)
  108. path = os.path.join(base, *resource)
  109. if os.path.exists(path):
  110. yield path
  111. else:
  112. def save_config_path(*resource):
  113. import xdg.BaseDirectory
  114. return xdg.BaseDirectory.save_config_path(*resource)
  115. def load_config_paths(*resource):
  116. import xdg.BaseDirectory
  117. return xdg.BaseDirectory.load_config_paths(*resource)
  118. def matches_requirement(req, wheels):
  119. """List of wheels matching a requirement.
  120. :param req: The requirement to satisfy
  121. :param wheels: List of wheels to search.
  122. """
  123. try:
  124. from pkg_resources import Distribution, Requirement
  125. except ImportError:
  126. raise RuntimeError("Cannot use requirements without pkg_resources")
  127. req = Requirement.parse(req)
  128. selected = []
  129. for wf in wheels:
  130. f = wf.parsed_filename
  131. dist = Distribution(project_name=f.group("name"), version=f.group("ver"))
  132. if dist in req:
  133. selected.append(wf)
  134. return selected

Powered by TurnKey Linux.