PageRenderTime 28ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/site-packages/wheel/metadata.py

https://gitlab.com/areema/myproject
Python | 317 lines | 303 code | 8 blank | 6 comment | 3 complexity | e0adfa93050df16ea9e22d064682827d MD5 | raw file
  1. """
  2. Tools for converting old- to new-style metadata.
  3. """
  4. from collections import namedtuple
  5. from .pkginfo import read_pkg_info
  6. from .util import OrderedDefaultDict
  7. try:
  8. from collections import OrderedDict
  9. except ImportError:
  10. OrderedDict = dict
  11. import re
  12. import os.path
  13. import textwrap
  14. import pkg_resources
  15. import email.parser
  16. import wheel
  17. METADATA_VERSION = "2.0"
  18. PLURAL_FIELDS = { "classifier" : "classifiers",
  19. "provides_dist" : "provides",
  20. "provides_extra" : "extras" }
  21. SKIP_FIELDS = set()
  22. CONTACT_FIELDS = (({"email":"author_email", "name": "author"},
  23. "author"),
  24. ({"email":"maintainer_email", "name": "maintainer"},
  25. "maintainer"))
  26. # commonly filled out as "UNKNOWN" by distutils:
  27. UNKNOWN_FIELDS = set(("author", "author_email", "platform", "home_page",
  28. "license"))
  29. # Wheel itself is probably the only program that uses non-extras markers
  30. # in METADATA/PKG-INFO. Support its syntax with the extra at the end only.
  31. EXTRA_RE = re.compile("""^(?P<package>.*?)(;\s*(?P<condition>.*?)(extra == '(?P<extra>.*?)')?)$""")
  32. KEYWORDS_RE = re.compile("[\0-,]+")
  33. MayRequiresKey = namedtuple('MayRequiresKey', ('condition', 'extra'))
  34. def unique(iterable):
  35. """
  36. Yield unique values in iterable, preserving order.
  37. """
  38. seen = set()
  39. for value in iterable:
  40. if not value in seen:
  41. seen.add(value)
  42. yield value
  43. def handle_requires(metadata, pkg_info, key):
  44. """
  45. Place the runtime requirements from pkg_info into metadata.
  46. """
  47. may_requires = OrderedDefaultDict(list)
  48. for value in sorted(pkg_info.get_all(key)):
  49. extra_match = EXTRA_RE.search(value)
  50. if extra_match:
  51. groupdict = extra_match.groupdict()
  52. condition = groupdict['condition']
  53. extra = groupdict['extra']
  54. package = groupdict['package']
  55. if condition.endswith(' and '):
  56. condition = condition[:-5]
  57. else:
  58. condition, extra = None, None
  59. package = value
  60. key = MayRequiresKey(condition, extra)
  61. may_requires[key].append(package)
  62. if may_requires:
  63. metadata['run_requires'] = []
  64. def sort_key(item):
  65. # Both condition and extra could be None, which can't be compared
  66. # against strings in Python 3.
  67. key, value = item
  68. if key.condition is None:
  69. return ''
  70. return key.condition
  71. for key, value in sorted(may_requires.items(), key=sort_key):
  72. may_requirement = OrderedDict((('requires', value),))
  73. if key.extra:
  74. may_requirement['extra'] = key.extra
  75. if key.condition:
  76. may_requirement['environment'] = key.condition
  77. metadata['run_requires'].append(may_requirement)
  78. if not 'extras' in metadata:
  79. metadata['extras'] = []
  80. metadata['extras'].extend([key.extra for key in may_requires.keys() if key.extra])
  81. def pkginfo_to_dict(path, distribution=None):
  82. """
  83. Convert PKG-INFO to a prototype Metadata 2.0 (PEP 426) dict.
  84. The description is included under the key ['description'] rather than
  85. being written to a separate file.
  86. path: path to PKG-INFO file
  87. distribution: optional distutils Distribution()
  88. """
  89. metadata = OrderedDefaultDict(lambda: OrderedDefaultDict(lambda: OrderedDefaultDict(OrderedDict)))
  90. metadata["generator"] = "bdist_wheel (" + wheel.__version__ + ")"
  91. try:
  92. unicode
  93. pkg_info = read_pkg_info(path)
  94. except NameError:
  95. pkg_info = email.parser.Parser().parsestr(open(path, 'rb').read().decode('utf-8'))
  96. description = None
  97. if pkg_info['Summary']:
  98. metadata['summary'] = pkginfo_unicode(pkg_info, 'Summary')
  99. del pkg_info['Summary']
  100. if pkg_info['Description']:
  101. description = dedent_description(pkg_info)
  102. del pkg_info['Description']
  103. else:
  104. payload = pkg_info.get_payload()
  105. if isinstance(payload, bytes):
  106. # Avoid a Python 2 Unicode error.
  107. # We still suffer ? glyphs on Python 3.
  108. payload = payload.decode('utf-8')
  109. if payload:
  110. description = payload
  111. if description:
  112. pkg_info['description'] = description
  113. for key in sorted(unique(k.lower() for k in pkg_info.keys())):
  114. low_key = key.replace('-', '_')
  115. if low_key in SKIP_FIELDS:
  116. continue
  117. if low_key in UNKNOWN_FIELDS and pkg_info.get(key) == 'UNKNOWN':
  118. continue
  119. if low_key in sorted(PLURAL_FIELDS):
  120. metadata[PLURAL_FIELDS[low_key]] = pkg_info.get_all(key)
  121. elif low_key == "requires_dist":
  122. handle_requires(metadata, pkg_info, key)
  123. elif low_key == 'provides_extra':
  124. if not 'extras' in metadata:
  125. metadata['extras'] = []
  126. metadata['extras'].extend(pkg_info.get_all(key))
  127. elif low_key == 'home_page':
  128. metadata['extensions']['python.details']['project_urls'] = {'Home':pkg_info[key]}
  129. elif low_key == 'keywords':
  130. metadata['keywords'] = KEYWORDS_RE.split(pkg_info[key])
  131. else:
  132. metadata[low_key] = pkg_info[key]
  133. metadata['metadata_version'] = METADATA_VERSION
  134. if 'extras' in metadata:
  135. metadata['extras'] = sorted(set(metadata['extras']))
  136. # include more information if distribution is available
  137. if distribution:
  138. for requires, attr in (('test_requires', 'tests_require'),):
  139. try:
  140. requirements = getattr(distribution, attr)
  141. if isinstance(requirements, list):
  142. new_requirements = sorted(convert_requirements(requirements))
  143. metadata[requires] = [{'requires':new_requirements}]
  144. except AttributeError:
  145. pass
  146. # handle contacts
  147. contacts = []
  148. for contact_type, role in CONTACT_FIELDS:
  149. contact = OrderedDict()
  150. for key in sorted(contact_type):
  151. if contact_type[key] in metadata:
  152. contact[key] = metadata.pop(contact_type[key])
  153. if contact:
  154. contact['role'] = role
  155. contacts.append(contact)
  156. if contacts:
  157. metadata['extensions']['python.details']['contacts'] = contacts
  158. # convert entry points to exports
  159. try:
  160. with open(os.path.join(os.path.dirname(path), "entry_points.txt"), "r") as ep_file:
  161. ep_map = pkg_resources.EntryPoint.parse_map(ep_file.read())
  162. exports = OrderedDict()
  163. for group, items in sorted(ep_map.items()):
  164. exports[group] = OrderedDict()
  165. for item in sorted(map(str, items.values())):
  166. name, export = item.split(' = ', 1)
  167. exports[group][name] = export
  168. if exports:
  169. metadata['extensions']['python.exports'] = exports
  170. except IOError:
  171. pass
  172. # copy console_scripts entry points to commands
  173. if 'python.exports' in metadata['extensions']:
  174. for (ep_script, wrap_script) in (('console_scripts', 'wrap_console'),
  175. ('gui_scripts', 'wrap_gui')):
  176. if ep_script in metadata['extensions']['python.exports']:
  177. metadata['extensions']['python.commands'][wrap_script] = \
  178. metadata['extensions']['python.exports'][ep_script]
  179. return metadata
  180. def requires_to_requires_dist(requirement):
  181. """Compose the version predicates for requirement in PEP 345 fashion."""
  182. requires_dist = []
  183. for op, ver in requirement.specs:
  184. requires_dist.append(op + ver)
  185. if not requires_dist:
  186. return ''
  187. return " (%s)" % ','.join(requires_dist)
  188. def convert_requirements(requirements):
  189. """Yield Requires-Dist: strings for parsed requirements strings."""
  190. for req in requirements:
  191. parsed_requirement = pkg_resources.Requirement.parse(req)
  192. spec = requires_to_requires_dist(parsed_requirement)
  193. extras = ",".join(parsed_requirement.extras)
  194. if extras:
  195. extras = "[%s]" % extras
  196. yield (parsed_requirement.project_name + extras + spec)
  197. def pkginfo_to_metadata(egg_info_path, pkginfo_path):
  198. """
  199. Convert .egg-info directory with PKG-INFO to the Metadata 1.3 aka
  200. old-draft Metadata 2.0 format.
  201. """
  202. pkg_info = read_pkg_info(pkginfo_path)
  203. pkg_info.replace_header('Metadata-Version', '2.0')
  204. requires_path = os.path.join(egg_info_path, 'requires.txt')
  205. if os.path.exists(requires_path):
  206. requires = open(requires_path).read()
  207. for extra, reqs in sorted(pkg_resources.split_sections(requires),
  208. key=lambda x: x[0] or ''):
  209. condition = ''
  210. if extra and ':' in extra: # setuptools extra:condition syntax
  211. extra, condition = extra.split(':', 1)
  212. if extra:
  213. pkg_info['Provides-Extra'] = extra
  214. if condition:
  215. condition += " and "
  216. condition += 'extra == %s' % repr(extra)
  217. if condition:
  218. condition = '; ' + condition
  219. for new_req in sorted(convert_requirements(reqs)):
  220. pkg_info['Requires-Dist'] = new_req + condition
  221. description = pkg_info['Description']
  222. if description:
  223. pkg_info.set_payload(dedent_description(pkg_info))
  224. del pkg_info['Description']
  225. return pkg_info
  226. def pkginfo_unicode(pkg_info, field):
  227. """Hack to coax Unicode out of an email Message() - Python 3.3+"""
  228. text = pkg_info[field]
  229. field = field.lower()
  230. if not isinstance(text, str):
  231. if not hasattr(pkg_info, 'raw_items'): # Python 3.2
  232. return str(text)
  233. for item in pkg_info.raw_items():
  234. if item[0].lower() == field:
  235. text = item[1].encode('ascii', 'surrogateescape')\
  236. .decode('utf-8')
  237. break
  238. return text
  239. def dedent_description(pkg_info):
  240. """
  241. Dedent and convert pkg_info['Description'] to Unicode.
  242. """
  243. description = pkg_info['Description']
  244. # Python 3 Unicode handling, sorta.
  245. surrogates = False
  246. if not isinstance(description, str):
  247. surrogates = True
  248. description = pkginfo_unicode(pkg_info, 'Description')
  249. description_lines = description.splitlines()
  250. description_dedent = '\n'.join(
  251. # if the first line of long_description is blank,
  252. # the first line here will be indented.
  253. (description_lines[0].lstrip(),
  254. textwrap.dedent('\n'.join(description_lines[1:])),
  255. '\n'))
  256. if surrogates:
  257. description_dedent = description_dedent\
  258. .encode("utf8")\
  259. .decode("ascii", "surrogateescape")
  260. return description_dedent
  261. if __name__ == "__main__":
  262. import sys, pprint
  263. pprint.pprint(pkginfo_to_dict(sys.argv[1]))