com.googler.python 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/package.json +4 -2
  2. package/python3.4.2/lib/python3.4/site-packages/pip/__init__.py +1 -277
  3. package/python3.4.2/lib/python3.4/site-packages/pip/__main__.py +19 -7
  4. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/__init__.py +246 -0
  5. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/basecommand.py +373 -0
  6. package/python3.4.2/lib/python3.4/site-packages/pip/{baseparser.py → _internal/baseparser.py} +240 -224
  7. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/build_env.py +92 -0
  8. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/cache.py +202 -0
  9. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/cmdoptions.py +609 -0
  10. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/__init__.py +79 -0
  11. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/check.py +42 -0
  12. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/completion.py +94 -0
  13. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/configuration.py +227 -0
  14. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/download.py +233 -0
  15. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/freeze.py +96 -0
  16. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/hash.py +57 -0
  17. package/python3.4.2/lib/python3.4/site-packages/pip/{commands → _internal/commands}/help.py +36 -33
  18. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/install.py +477 -0
  19. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/list.py +343 -0
  20. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/search.py +135 -0
  21. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/show.py +164 -0
  22. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/uninstall.py +71 -0
  23. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/commands/wheel.py +179 -0
  24. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/compat.py +235 -0
  25. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/configuration.py +378 -0
  26. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/download.py +922 -0
  27. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/exceptions.py +249 -0
  28. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/index.py +1117 -0
  29. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/locations.py +194 -0
  30. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/models/__init__.py +4 -0
  31. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/models/index.py +15 -0
  32. package/python3.4.2/lib/python3.4/site-packages/pip/{_vendor/requests/packages/urllib3/contrib → _internal/operations}/__init__.py +0 -0
  33. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/operations/check.py +106 -0
  34. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/operations/freeze.py +252 -0
  35. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/operations/prepare.py +378 -0
  36. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/pep425tags.py +317 -0
  37. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/req/__init__.py +69 -0
  38. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/req/req_file.py +338 -0
  39. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/req/req_install.py +1115 -0
  40. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/req/req_set.py +164 -0
  41. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/req/req_uninstall.py +455 -0
  42. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/resolve.py +354 -0
  43. package/python3.4.2/lib/python3.4/site-packages/pip/{status_codes.py → _internal/status_codes.py} +8 -6
  44. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/__init__.py +0 -0
  45. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/appdirs.py +258 -0
  46. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/deprecation.py +77 -0
  47. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/encoding.py +33 -0
  48. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/filesystem.py +28 -0
  49. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/glibc.py +84 -0
  50. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/hashes.py +94 -0
  51. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/logging.py +132 -0
  52. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/misc.py +851 -0
  53. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/outdated.py +163 -0
  54. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/packaging.py +70 -0
  55. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/setuptools_build.py +8 -0
  56. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/temp_dir.py +82 -0
  57. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/typing.py +29 -0
  58. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/utils/ui.py +421 -0
  59. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/vcs/__init__.py +471 -0
  60. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/vcs/bazaar.py +113 -0
  61. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/vcs/git.py +311 -0
  62. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/vcs/mercurial.py +105 -0
  63. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/vcs/subversion.py +271 -0
  64. package/python3.4.2/lib/python3.4/site-packages/pip/_internal/wheel.py +817 -0
  65. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/__init__.py +109 -8
  66. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/appdirs.py +604 -0
  67. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/__init__.py +11 -0
  68. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/_cmd.py +60 -0
  69. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/adapter.py +134 -0
  70. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/cache.py +39 -0
  71. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +2 -0
  72. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +133 -0
  73. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +43 -0
  74. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/compat.py +29 -0
  75. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/controller.py +373 -0
  76. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/filewrapper.py +78 -0
  77. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/heuristics.py +138 -0
  78. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/serialize.py +194 -0
  79. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/cachecontrol/wrapper.py +27 -0
  80. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/certifi/__init__.py +3 -0
  81. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/certifi/__main__.py +2 -0
  82. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests → certifi}/cacert.pem +1765 -2358
  83. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/certifi/core.py +37 -0
  84. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/__init__.py +39 -32
  85. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/big5freq.py +386 -0
  86. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/big5prober.py +47 -42
  87. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/chardistribution.py +233 -231
  88. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/charsetgroupprober.py +106 -0
  89. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/charsetprober.py +145 -0
  90. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/cli/__init__.py +1 -0
  91. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/cli/chardetect.py +85 -0
  92. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/codingstatemachine.py +88 -0
  93. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/compat.py +34 -34
  94. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/cp949prober.py +49 -44
  95. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/enums.py +76 -0
  96. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/escprober.py +101 -0
  97. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/escsm.py +246 -0
  98. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/eucjpprober.py +92 -0
  99. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/euckrfreq.py +195 -0
  100. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/euckrprober.py +47 -42
  101. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/euctwfreq.py +387 -428
  102. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/euctwprober.py +46 -41
  103. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/gb2312freq.py +283 -472
  104. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/gb2312prober.py +46 -41
  105. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/hebrewprober.py +292 -283
  106. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/jisfreq.py +325 -569
  107. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/jpcntx.py +233 -219
  108. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langbulgarianmodel.py +228 -229
  109. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langcyrillicmodel.py +333 -329
  110. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langgreekmodel.py +225 -225
  111. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langhebrewmodel.py +200 -201
  112. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langhungarianmodel.py +225 -225
  113. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/langthaimodel.py +199 -200
  114. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/langturkishmodel.py +193 -0
  115. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/latin1prober.py +145 -139
  116. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/mbcharsetprober.py +91 -0
  117. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/mbcsgroupprober.py +54 -54
  118. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/mbcssm.py +572 -0
  119. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/sbcharsetprober.py +132 -0
  120. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/sbcsgroupprober.py +73 -69
  121. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/sjisprober.py +92 -0
  122. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/universaldetector.py +286 -0
  123. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/chardet → chardet}/utf8prober.py +82 -76
  124. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/chardet/version.py +9 -0
  125. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/__init__.py +7 -7
  126. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/ansi.py +102 -50
  127. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/ansitowin32.py +236 -190
  128. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/initialise.py +82 -56
  129. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/win32.py +156 -137
  130. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/colorama/winterm.py +162 -120
  131. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/__init__.py +23 -23
  132. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/__init__.py +6 -6
  133. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/misc.py +41 -41
  134. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/shutil.py +761 -761
  135. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg +84 -84
  136. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/sysconfig.py +788 -788
  137. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/_backport/tarfile.py +2607 -2607
  138. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/compat.py +1117 -1064
  139. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/database.py +1318 -1301
  140. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/index.py +516 -488
  141. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/locators.py +1292 -1194
  142. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/manifest.py +393 -364
  143. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/markers.py +131 -190
  144. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/metadata.py +1068 -1026
  145. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/resources.py +355 -317
  146. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/scripts.py +415 -323
  147. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/t32.exe +0 -0
  148. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/t64.exe +0 -0
  149. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/util.py +1755 -1575
  150. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/version.py +736 -721
  151. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/w32.exe +0 -0
  152. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/w64.exe +0 -0
  153. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distlib/wheel.py +984 -958
  154. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/distro.py +1104 -0
  155. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/__init__.py +35 -23
  156. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{ihatexml.py → _ihatexml.py} +288 -285
  157. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{inputstream.py → _inputstream.py} +923 -881
  158. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{tokenizer.py → _tokenizer.py} +1721 -1731
  159. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{trie → _trie}/__init__.py +14 -12
  160. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{trie → _trie}/_base.py +37 -37
  161. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{trie → _trie}/datrie.py +44 -44
  162. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{trie → _trie}/py.py +67 -67
  163. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/{utils.py → _utils.py} +124 -82
  164. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/constants.py +2947 -3104
  165. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py +29 -20
  166. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/{_base.py → base.py} +12 -12
  167. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py +73 -65
  168. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/lint.py +93 -93
  169. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/optionaltags.py +207 -205
  170. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/sanitizer.py +896 -12
  171. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/filters/whitespace.py +38 -38
  172. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/html5parser.py +2791 -2713
  173. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/serializer.py +409 -0
  174. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py +30 -0
  175. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py +54 -0
  176. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treeadapters/sax.py +50 -44
  177. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py +88 -76
  178. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treebuilders/{_base.py → base.py} +417 -377
  179. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treebuilders/dom.py +236 -227
  180. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treebuilders/etree.py +340 -337
  181. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py +366 -369
  182. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py +154 -57
  183. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/{_base.py → base.py} +252 -200
  184. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/dom.py +43 -46
  185. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/etree.py +130 -138
  186. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/{lxmletree.py → etree_lxml.py} +213 -208
  187. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/{genshistream.py → genshi.py} +69 -69
  188. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/__init__.py +2 -0
  189. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/codec.py +118 -0
  190. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/compat.py +12 -0
  191. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/core.py +387 -0
  192. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/idnadata.py +1585 -0
  193. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/intranges.py +53 -0
  194. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/package_data.py +2 -0
  195. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/idna/uts46data.py +7634 -0
  196. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/ipaddress.py +2419 -0
  197. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/__init__.py +347 -0
  198. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/linklockfile.py +73 -0
  199. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/mkdirlockfile.py +84 -0
  200. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/pidlockfile.py +190 -0
  201. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/sqlitelockfile.py +156 -0
  202. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/lockfile/symlinklockfile.py +70 -0
  203. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/msgpack/__init__.py +66 -0
  204. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/msgpack/_version.py +1 -0
  205. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/msgpack/exceptions.py +41 -0
  206. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/msgpack/fallback.py +971 -0
  207. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/__about__.py +21 -0
  208. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/__init__.py +14 -0
  209. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/_compat.py +30 -0
  210. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/_structures.py +70 -0
  211. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/markers.py +301 -0
  212. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/requirements.py +130 -0
  213. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/specifiers.py +774 -0
  214. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/utils.py +63 -0
  215. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/packaging/version.py +441 -0
  216. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{pkg_resources.py → pkg_resources/__init__.py} +3125 -2762
  217. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pkg_resources/py31compat.py +22 -0
  218. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/progress/__init__.py +127 -0
  219. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/progress/bar.py +88 -0
  220. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/progress/counter.py +48 -0
  221. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/progress/helpers.py +91 -0
  222. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/progress/spinner.py +44 -0
  223. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pyparsing.py +5720 -0
  224. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pytoml/__init__.py +3 -0
  225. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pytoml/core.py +13 -0
  226. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pytoml/parser.py +374 -0
  227. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/pytoml/writer.py +127 -0
  228. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/__init__.py +123 -77
  229. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/__version__.py +14 -0
  230. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/_internal_utils.py +42 -0
  231. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/adapters.py +525 -388
  232. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/api.py +152 -120
  233. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/auth.py +293 -193
  234. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/certs.py +18 -24
  235. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/compat.py +73 -115
  236. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/cookies.py +542 -454
  237. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/exceptions.py +122 -75
  238. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/help.py +120 -0
  239. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/hooks.py +34 -45
  240. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/models.py +948 -803
  241. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages.py +16 -0
  242. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/sessions.py +737 -637
  243. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/status_codes.py +91 -88
  244. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/structures.py +105 -127
  245. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/utils.py +904 -673
  246. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/retrying.py +267 -0
  247. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/six.py +891 -646
  248. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/__init__.py +97 -0
  249. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/_collections.py +319 -0
  250. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/connection.py +373 -0
  251. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/connectionpool.py +905 -710
  252. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/__init__.py +0 -0
  253. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py +0 -0
  254. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py +593 -0
  255. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py +343 -0
  256. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/appengine.py +296 -0
  257. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/contrib/ntlmpool.py +112 -120
  258. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +455 -0
  259. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +810 -0
  260. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/contrib/socks.py +188 -0
  261. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/exceptions.py +246 -0
  262. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/fields.py +178 -177
  263. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/filepost.py +94 -100
  264. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/packages/__init__.py +5 -4
  265. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py +0 -0
  266. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py +53 -0
  267. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/packages/ordered_dict.py +259 -260
  268. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/packages/six.py +868 -0
  269. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/packages/ssl_match_hostname/__init__.py +19 -13
  270. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/packages/ssl_match_hostname/_implementation.py +157 -105
  271. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/poolmanager.py +440 -0
  272. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/request.py +148 -141
  273. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/response.py +626 -0
  274. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/__init__.py +54 -0
  275. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/connection.py +130 -0
  276. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/request.py +118 -0
  277. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/response.py +81 -0
  278. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/retry.py +401 -0
  279. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/selectors.py +581 -0
  280. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/ssl_.py +341 -0
  281. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/util/timeout.py +242 -234
  282. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/{requests/packages/urllib3 → urllib3}/util/url.py +230 -162
  283. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/urllib3/util/wait.py +40 -0
  284. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/webencodings/__init__.py +342 -0
  285. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/webencodings/labels.py +231 -0
  286. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/webencodings/mklabels.py +59 -0
  287. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/webencodings/tests.py +153 -0
  288. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/webencodings/x_user_defined.py +325 -0
  289. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/_markerlib/__init__.py +0 -16
  290. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/_markerlib/markers.py +0 -119
  291. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/sanitizer.py +0 -271
  292. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/serializer/__init__.py +0 -16
  293. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/serializer/htmlserializer.py +0 -320
  294. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/html5lib/treewalkers/pulldom.py +0 -63
  295. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/re-vendor.py +0 -34
  296. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/__init__.py +0 -3
  297. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/big5freq.py +0 -925
  298. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/chardetect.py +0 -46
  299. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/charsetgroupprober.py +0 -106
  300. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/charsetprober.py +0 -62
  301. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/codingstatemachine.py +0 -61
  302. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/constants.py +0 -39
  303. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/escprober.py +0 -86
  304. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/escsm.py +0 -242
  305. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/eucjpprober.py +0 -90
  306. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/euckrfreq.py +0 -596
  307. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/mbcharsetprober.py +0 -86
  308. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/mbcssm.py +0 -575
  309. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/sbcharsetprober.py +0 -120
  310. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/sjisprober.py +0 -91
  311. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/chardet/universaldetector.py +0 -170
  312. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/__init__.py +0 -58
  313. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/_collections.py +0 -205
  314. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/connection.py +0 -204
  315. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py +0 -422
  316. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/exceptions.py +0 -126
  317. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/packages/six.py +0 -385
  318. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/poolmanager.py +0 -258
  319. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/response.py +0 -308
  320. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/util/__init__.py +0 -27
  321. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/util/connection.py +0 -45
  322. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/util/request.py +0 -68
  323. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/util/response.py +0 -13
  324. package/python3.4.2/lib/python3.4/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py +0 -133
  325. package/python3.4.2/lib/python3.4/site-packages/pip/backwardcompat/__init__.py +0 -138
  326. package/python3.4.2/lib/python3.4/site-packages/pip/basecommand.py +0 -201
  327. package/python3.4.2/lib/python3.4/site-packages/pip/cmdoptions.py +0 -371
  328. package/python3.4.2/lib/python3.4/site-packages/pip/commands/__init__.py +0 -88
  329. package/python3.4.2/lib/python3.4/site-packages/pip/commands/bundle.py +0 -42
  330. package/python3.4.2/lib/python3.4/site-packages/pip/commands/completion.py +0 -59
  331. package/python3.4.2/lib/python3.4/site-packages/pip/commands/freeze.py +0 -114
  332. package/python3.4.2/lib/python3.4/site-packages/pip/commands/install.py +0 -314
  333. package/python3.4.2/lib/python3.4/site-packages/pip/commands/list.py +0 -162
  334. package/python3.4.2/lib/python3.4/site-packages/pip/commands/search.py +0 -132
  335. package/python3.4.2/lib/python3.4/site-packages/pip/commands/show.py +0 -80
  336. package/python3.4.2/lib/python3.4/site-packages/pip/commands/uninstall.py +0 -59
  337. package/python3.4.2/lib/python3.4/site-packages/pip/commands/unzip.py +0 -7
  338. package/python3.4.2/lib/python3.4/site-packages/pip/commands/wheel.py +0 -195
  339. package/python3.4.2/lib/python3.4/site-packages/pip/commands/zip.py +0 -351
  340. package/python3.4.2/lib/python3.4/site-packages/pip/download.py +0 -644
  341. package/python3.4.2/lib/python3.4/site-packages/pip/exceptions.py +0 -46
  342. package/python3.4.2/lib/python3.4/site-packages/pip/index.py +0 -990
  343. package/python3.4.2/lib/python3.4/site-packages/pip/locations.py +0 -171
  344. package/python3.4.2/lib/python3.4/site-packages/pip/log.py +0 -276
  345. package/python3.4.2/lib/python3.4/site-packages/pip/pep425tags.py +0 -102
  346. package/python3.4.2/lib/python3.4/site-packages/pip/req.py +0 -1931
  347. package/python3.4.2/lib/python3.4/site-packages/pip/runner.py +0 -18
  348. package/python3.4.2/lib/python3.4/site-packages/pip/util.py +0 -720
  349. package/python3.4.2/lib/python3.4/site-packages/pip/vcs/__init__.py +0 -251
  350. package/python3.4.2/lib/python3.4/site-packages/pip/vcs/bazaar.py +0 -131
  351. package/python3.4.2/lib/python3.4/site-packages/pip/vcs/git.py +0 -194
  352. package/python3.4.2/lib/python3.4/site-packages/pip/vcs/mercurial.py +0 -151
  353. package/python3.4.2/lib/python3.4/site-packages/pip/vcs/subversion.py +0 -273
  354. package/python3.4.2/lib/python3.4/site-packages/pip/wheel.py +0 -560
@@ -1,1301 +1,1318 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2012-2013 The Python Software Foundation.
4
- # See LICENSE.txt and CONTRIBUTORS.txt.
5
- #
6
- """PEP 376 implementation."""
7
-
8
- from __future__ import unicode_literals
9
-
10
- import base64
11
- import codecs
12
- import contextlib
13
- import hashlib
14
- import logging
15
- import os
16
- import posixpath
17
- import sys
18
- import zipimport
19
-
20
- from . import DistlibException, resources
21
- from .compat import StringIO
22
- from .version import get_scheme, UnsupportedVersionError
23
- from .metadata import Metadata, METADATA_FILENAME
24
- from .util import (parse_requirement, cached_property, parse_name_and_version,
25
- read_exports, write_exports, CSVReader, CSVWriter)
26
-
27
-
28
- __all__ = ['Distribution', 'BaseInstalledDistribution',
29
- 'InstalledDistribution', 'EggInfoDistribution',
30
- 'DistributionPath']
31
-
32
-
33
- logger = logging.getLogger(__name__)
34
-
35
- EXPORTS_FILENAME = 'pydist-exports.json'
36
- COMMANDS_FILENAME = 'pydist-commands.json'
37
-
38
- DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED',
39
- 'RESOURCES', EXPORTS_FILENAME, 'SHARED')
40
-
41
- DISTINFO_EXT = '.dist-info'
42
-
43
-
44
- class _Cache(object):
45
- """
46
- A simple cache mapping names and .dist-info paths to distributions
47
- """
48
- def __init__(self):
49
- """
50
- Initialise an instance. There is normally one for each DistributionPath.
51
- """
52
- self.name = {}
53
- self.path = {}
54
- self.generated = False
55
-
56
- def clear(self):
57
- """
58
- Clear the cache, setting it to its initial state.
59
- """
60
- self.name.clear()
61
- self.path.clear()
62
- self.generated = False
63
-
64
- def add(self, dist):
65
- """
66
- Add a distribution to the cache.
67
- :param dist: The distribution to add.
68
- """
69
- if dist.path not in self.path:
70
- self.path[dist.path] = dist
71
- self.name.setdefault(dist.key, []).append(dist)
72
-
73
-
74
- class DistributionPath(object):
75
- """
76
- Represents a set of distributions installed on a path (typically sys.path).
77
- """
78
- def __init__(self, path=None, include_egg=False):
79
- """
80
- Create an instance from a path, optionally including legacy (distutils/
81
- setuptools/distribute) distributions.
82
- :param path: The path to use, as a list of directories. If not specified,
83
- sys.path is used.
84
- :param include_egg: If True, this instance will look for and return legacy
85
- distributions as well as those based on PEP 376.
86
- """
87
- if path is None:
88
- path = sys.path
89
- self.path = path
90
- self._include_dist = True
91
- self._include_egg = include_egg
92
-
93
- self._cache = _Cache()
94
- self._cache_egg = _Cache()
95
- self._cache_enabled = True
96
- self._scheme = get_scheme('default')
97
-
98
- def _get_cache_enabled(self):
99
- return self._cache_enabled
100
-
101
- def _set_cache_enabled(self, value):
102
- self._cache_enabled = value
103
-
104
- cache_enabled = property(_get_cache_enabled, _set_cache_enabled)
105
-
106
- def clear_cache(self):
107
- """
108
- Clears the internal cache.
109
- """
110
- self._cache.clear()
111
- self._cache_egg.clear()
112
-
113
-
114
- def _yield_distributions(self):
115
- """
116
- Yield .dist-info and/or .egg(-info) distributions.
117
- """
118
- # We need to check if we've seen some resources already, because on
119
- # some Linux systems (e.g. some Debian/Ubuntu variants) there are
120
- # symlinks which alias other files in the environment.
121
- seen = set()
122
- for path in self.path:
123
- finder = resources.finder_for_path(path)
124
- if finder is None:
125
- continue
126
- r = finder.find('')
127
- if not r or not r.is_container:
128
- continue
129
- rset = sorted(r.resources)
130
- for entry in rset:
131
- r = finder.find(entry)
132
- if not r or r.path in seen:
133
- continue
134
- if self._include_dist and entry.endswith(DISTINFO_EXT):
135
- metadata_path = posixpath.join(entry, METADATA_FILENAME)
136
- pydist = finder.find(metadata_path)
137
- if not pydist:
138
- continue
139
-
140
- metadata = Metadata(fileobj=pydist.as_stream(),
141
- scheme='legacy')
142
- logger.debug('Found %s', r.path)
143
- seen.add(r.path)
144
- yield new_dist_class(r.path, metadata=metadata,
145
- env=self)
146
- elif self._include_egg and entry.endswith(('.egg-info',
147
- '.egg')):
148
- logger.debug('Found %s', r.path)
149
- seen.add(r.path)
150
- yield old_dist_class(r.path, self)
151
-
152
- def _generate_cache(self):
153
- """
154
- Scan the path for distributions and populate the cache with
155
- those that are found.
156
- """
157
- gen_dist = not self._cache.generated
158
- gen_egg = self._include_egg and not self._cache_egg.generated
159
- if gen_dist or gen_egg:
160
- for dist in self._yield_distributions():
161
- if isinstance(dist, InstalledDistribution):
162
- self._cache.add(dist)
163
- else:
164
- self._cache_egg.add(dist)
165
-
166
- if gen_dist:
167
- self._cache.generated = True
168
- if gen_egg:
169
- self._cache_egg.generated = True
170
-
171
- @classmethod
172
- def distinfo_dirname(cls, name, version):
173
- """
174
- The *name* and *version* parameters are converted into their
175
- filename-escaped form, i.e. any ``'-'`` characters are replaced
176
- with ``'_'`` other than the one in ``'dist-info'`` and the one
177
- separating the name from the version number.
178
-
179
- :parameter name: is converted to a standard distribution name by replacing
180
- any runs of non- alphanumeric characters with a single
181
- ``'-'``.
182
- :type name: string
183
- :parameter version: is converted to a standard version string. Spaces
184
- become dots, and all other non-alphanumeric characters
185
- (except dots) become dashes, with runs of multiple
186
- dashes condensed to a single dash.
187
- :type version: string
188
- :returns: directory name
189
- :rtype: string"""
190
- name = name.replace('-', '_')
191
- return '-'.join([name, version]) + DISTINFO_EXT
192
-
193
- def get_distributions(self):
194
- """
195
- Provides an iterator that looks for distributions and returns
196
- :class:`InstalledDistribution` or
197
- :class:`EggInfoDistribution` instances for each one of them.
198
-
199
- :rtype: iterator of :class:`InstalledDistribution` and
200
- :class:`EggInfoDistribution` instances
201
- """
202
- if not self._cache_enabled:
203
- for dist in self._yield_distributions():
204
- yield dist
205
- else:
206
- self._generate_cache()
207
-
208
- for dist in self._cache.path.values():
209
- yield dist
210
-
211
- if self._include_egg:
212
- for dist in self._cache_egg.path.values():
213
- yield dist
214
-
215
- def get_distribution(self, name):
216
- """
217
- Looks for a named distribution on the path.
218
-
219
- This function only returns the first result found, as no more than one
220
- value is expected. If nothing is found, ``None`` is returned.
221
-
222
- :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution`
223
- or ``None``
224
- """
225
- result = None
226
- name = name.lower()
227
- if not self._cache_enabled:
228
- for dist in self._yield_distributions():
229
- if dist.key == name:
230
- result = dist
231
- break
232
- else:
233
- self._generate_cache()
234
-
235
- if name in self._cache.name:
236
- result = self._cache.name[name][0]
237
- elif self._include_egg and name in self._cache_egg.name:
238
- result = self._cache_egg.name[name][0]
239
- return result
240
-
241
- def provides_distribution(self, name, version=None):
242
- """
243
- Iterates over all distributions to find which distributions provide *name*.
244
- If a *version* is provided, it will be used to filter the results.
245
-
246
- This function only returns the first result found, since no more than
247
- one values are expected. If the directory is not found, returns ``None``.
248
-
249
- :parameter version: a version specifier that indicates the version
250
- required, conforming to the format in ``PEP-345``
251
-
252
- :type name: string
253
- :type version: string
254
- """
255
- matcher = None
256
- if not version is None:
257
- try:
258
- matcher = self._scheme.matcher('%s (%s)' % (name, version))
259
- except ValueError:
260
- raise DistlibException('invalid name or version: %r, %r' %
261
- (name, version))
262
-
263
- for dist in self.get_distributions():
264
- provided = dist.provides
265
-
266
- for p in provided:
267
- p_name, p_ver = parse_name_and_version(p)
268
- if matcher is None:
269
- if p_name == name:
270
- yield dist
271
- break
272
- else:
273
- if p_name == name and matcher.match(p_ver):
274
- yield dist
275
- break
276
-
277
- def get_file_path(self, name, relative_path):
278
- """
279
- Return the path to a resource file.
280
- """
281
- dist = self.get_distribution(name)
282
- if dist is None:
283
- raise LookupError('no distribution named %r found' % name)
284
- return dist.get_resource_path(relative_path)
285
-
286
- def get_exported_entries(self, category, name=None):
287
- """
288
- Return all of the exported entries in a particular category.
289
-
290
- :param category: The category to search for entries.
291
- :param name: If specified, only entries with that name are returned.
292
- """
293
- for dist in self.get_distributions():
294
- r = dist.exports
295
- if category in r:
296
- d = r[category]
297
- if name is not None:
298
- if name in d:
299
- yield d[name]
300
- else:
301
- for v in d.values():
302
- yield v
303
-
304
-
305
- class Distribution(object):
306
- """
307
- A base class for distributions, whether installed or from indexes.
308
- Either way, it must have some metadata, so that's all that's needed
309
- for construction.
310
- """
311
-
312
- build_time_dependency = False
313
- """
314
- Set to True if it's known to be only a build-time dependency (i.e.
315
- not needed after installation).
316
- """
317
-
318
- requested = False
319
- """A boolean that indicates whether the ``REQUESTED`` metadata file is
320
- present (in other words, whether the package was installed by user
321
- request or it was installed as a dependency)."""
322
-
323
- def __init__(self, metadata):
324
- """
325
- Initialise an instance.
326
- :param metadata: The instance of :class:`Metadata` describing this
327
- distribution.
328
- """
329
- self.metadata = metadata
330
- self.name = metadata.name
331
- self.key = self.name.lower() # for case-insensitive comparisons
332
- self.version = metadata.version
333
- self.locator = None
334
- self.digest = None
335
- self.extras = None # additional features requested
336
- self.context = None # environment marker overrides
337
-
338
- @property
339
- def source_url(self):
340
- """
341
- The source archive download URL for this distribution.
342
- """
343
- return self.metadata.source_url
344
-
345
- download_url = source_url # Backward compatibility
346
-
347
- @property
348
- def name_and_version(self):
349
- """
350
- A utility property which displays the name and version in parentheses.
351
- """
352
- return '%s (%s)' % (self.name, self.version)
353
-
354
- @property
355
- def provides(self):
356
- """
357
- A set of distribution names and versions provided by this distribution.
358
- :return: A set of "name (version)" strings.
359
- """
360
- plist = self.metadata.provides
361
- s = '%s (%s)' % (self.name, self.version)
362
- if s not in plist:
363
- plist.append(s)
364
- return plist
365
-
366
- def _get_requirements(self, req_attr):
367
- reqts = getattr(self.metadata, req_attr)
368
- return set(self.metadata.get_requirements(reqts, extras=self.extras,
369
- env=self.context))
370
-
371
- @property
372
- def run_requires(self):
373
- return self._get_requirements('run_requires')
374
-
375
- @property
376
- def meta_requires(self):
377
- return self._get_requirements('meta_requires')
378
-
379
- @property
380
- def build_requires(self):
381
- return self._get_requirements('build_requires')
382
-
383
- @property
384
- def test_requires(self):
385
- return self._get_requirements('test_requires')
386
-
387
- @property
388
- def dev_requires(self):
389
- return self._get_requirements('dev_requires')
390
-
391
- def matches_requirement(self, req):
392
- """
393
- Say if this instance matches (fulfills) a requirement.
394
- :param req: The requirement to match.
395
- :rtype req: str
396
- :return: True if it matches, else False.
397
- """
398
- # Requirement may contain extras - parse to lose those
399
- # from what's passed to the matcher
400
- r = parse_requirement(req)
401
- scheme = get_scheme(self.metadata.scheme)
402
- try:
403
- matcher = scheme.matcher(r.requirement)
404
- except UnsupportedVersionError:
405
- # XXX compat-mode if cannot read the version
406
- logger.warning('could not read version %r - using name only',
407
- req)
408
- name = req.split()[0]
409
- matcher = scheme.matcher(name)
410
-
411
- name = matcher.key # case-insensitive
412
-
413
- result = False
414
- for p in self.provides:
415
- p_name, p_ver = parse_name_and_version(p)
416
- if p_name != name:
417
- continue
418
- try:
419
- result = matcher.match(p_ver)
420
- break
421
- except UnsupportedVersionError:
422
- pass
423
- return result
424
-
425
- def __repr__(self):
426
- """
427
- Return a textual representation of this instance,
428
- """
429
- if self.source_url:
430
- suffix = ' [%s]' % self.source_url
431
- else:
432
- suffix = ''
433
- return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix)
434
-
435
- def __eq__(self, other):
436
- """
437
- See if this distribution is the same as another.
438
- :param other: The distribution to compare with. To be equal to one
439
- another. distributions must have the same type, name,
440
- version and source_url.
441
- :return: True if it is the same, else False.
442
- """
443
- if type(other) is not type(self):
444
- result = False
445
- else:
446
- result = (self.name == other.name and
447
- self.version == other.version and
448
- self.source_url == other.source_url)
449
- return result
450
-
451
- def __hash__(self):
452
- """
453
- Compute hash in a way which matches the equality test.
454
- """
455
- return hash(self.name) + hash(self.version) + hash(self.source_url)
456
-
457
-
458
- class BaseInstalledDistribution(Distribution):
459
- """
460
- This is the base class for installed distributions (whether PEP 376 or
461
- legacy).
462
- """
463
-
464
- hasher = None
465
-
466
- def __init__(self, metadata, path, env=None):
467
- """
468
- Initialise an instance.
469
- :param metadata: An instance of :class:`Metadata` which describes the
470
- distribution. This will normally have been initialised
471
- from a metadata file in the ``path``.
472
- :param path: The path of the ``.dist-info`` or ``.egg-info``
473
- directory for the distribution.
474
- :param env: This is normally the :class:`DistributionPath`
475
- instance where this distribution was found.
476
- """
477
- super(BaseInstalledDistribution, self).__init__(metadata)
478
- self.path = path
479
- self.dist_path = env
480
-
481
- def get_hash(self, data, hasher=None):
482
- """
483
- Get the hash of some data, using a particular hash algorithm, if
484
- specified.
485
-
486
- :param data: The data to be hashed.
487
- :type data: bytes
488
- :param hasher: The name of a hash implementation, supported by hashlib,
489
- or ``None``. Examples of valid values are ``'sha1'``,
490
- ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and
491
- ``'sha512'``. If no hasher is specified, the ``hasher``
492
- attribute of the :class:`InstalledDistribution` instance
493
- is used. If the hasher is determined to be ``None``, MD5
494
- is used as the hashing algorithm.
495
- :returns: The hash of the data. If a hasher was explicitly specified,
496
- the returned hash will be prefixed with the specified hasher
497
- followed by '='.
498
- :rtype: str
499
- """
500
- if hasher is None:
501
- hasher = self.hasher
502
- if hasher is None:
503
- hasher = hashlib.md5
504
- prefix = ''
505
- else:
506
- hasher = getattr(hashlib, hasher)
507
- prefix = '%s=' % self.hasher
508
- digest = hasher(data).digest()
509
- digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii')
510
- return '%s%s' % (prefix, digest)
511
-
512
-
513
- class InstalledDistribution(BaseInstalledDistribution):
514
- """
515
- Created with the *path* of the ``.dist-info`` directory provided to the
516
- constructor. It reads the metadata contained in ``pydist.json`` when it is
517
- instantiated., or uses a passed in Metadata instance (useful for when
518
- dry-run mode is being used).
519
- """
520
-
521
- hasher = 'sha256'
522
-
523
- def __init__(self, path, metadata=None, env=None):
524
- self.finder = finder = resources.finder_for_path(path)
525
- if finder is None:
526
- import pdb; pdb.set_trace ()
527
- if env and env._cache_enabled and path in env._cache.path:
528
- metadata = env._cache.path[path].metadata
529
- elif metadata is None:
530
- r = finder.find(METADATA_FILENAME)
531
- # Temporary - for legacy support
532
- if r is None:
533
- r = finder.find('METADATA')
534
- if r is None:
535
- raise ValueError('no %s found in %s' % (METADATA_FILENAME,
536
- path))
537
- with contextlib.closing(r.as_stream()) as stream:
538
- metadata = Metadata(fileobj=stream, scheme='legacy')
539
-
540
- super(InstalledDistribution, self).__init__(metadata, path, env)
541
-
542
- if env and env._cache_enabled:
543
- env._cache.add(self)
544
-
545
- try:
546
- r = finder.find('REQUESTED')
547
- except AttributeError:
548
- import pdb; pdb.set_trace ()
549
- self.requested = r is not None
550
-
551
- def __repr__(self):
552
- return '<InstalledDistribution %r %s at %r>' % (
553
- self.name, self.version, self.path)
554
-
555
- def __str__(self):
556
- return "%s %s" % (self.name, self.version)
557
-
558
- def _get_records(self):
559
- """
560
- Get the list of installed files for the distribution
561
- :return: A list of tuples of path, hash and size. Note that hash and
562
- size might be ``None`` for some entries. The path is exactly
563
- as stored in the file (which is as in PEP 376).
564
- """
565
- results = []
566
- r = self.get_distinfo_resource('RECORD')
567
- with contextlib.closing(r.as_stream()) as stream:
568
- with CSVReader(stream=stream) as record_reader:
569
- # Base location is parent dir of .dist-info dir
570
- #base_location = os.path.dirname(self.path)
571
- #base_location = os.path.abspath(base_location)
572
- for row in record_reader:
573
- missing = [None for i in range(len(row), 3)]
574
- path, checksum, size = row + missing
575
- #if not os.path.isabs(path):
576
- # path = path.replace('/', os.sep)
577
- # path = os.path.join(base_location, path)
578
- results.append((path, checksum, size))
579
- return results
580
-
581
- @cached_property
582
- def exports(self):
583
- """
584
- Return the information exported by this distribution.
585
- :return: A dictionary of exports, mapping an export category to a dict
586
- of :class:`ExportEntry` instances describing the individual
587
- export entries, and keyed by name.
588
- """
589
- result = {}
590
- r = self.get_distinfo_resource(EXPORTS_FILENAME)
591
- if r:
592
- result = self.read_exports()
593
- return result
594
-
595
- def read_exports(self):
596
- """
597
- Read exports data from a file in .ini format.
598
-
599
- :return: A dictionary of exports, mapping an export category to a list
600
- of :class:`ExportEntry` instances describing the individual
601
- export entries.
602
- """
603
- result = {}
604
- r = self.get_distinfo_resource(EXPORTS_FILENAME)
605
- if r:
606
- with contextlib.closing(r.as_stream()) as stream:
607
- result = read_exports(stream)
608
- return result
609
-
610
- def write_exports(self, exports):
611
- """
612
- Write a dictionary of exports to a file in .ini format.
613
- :param exports: A dictionary of exports, mapping an export category to
614
- a list of :class:`ExportEntry` instances describing the
615
- individual export entries.
616
- """
617
- rf = self.get_distinfo_file(EXPORTS_FILENAME)
618
- with open(rf, 'w') as f:
619
- write_exports(exports, f)
620
-
621
- def get_resource_path(self, relative_path):
622
- """
623
- NOTE: This API may change in the future.
624
-
625
- Return the absolute path to a resource file with the given relative
626
- path.
627
-
628
- :param relative_path: The path, relative to .dist-info, of the resource
629
- of interest.
630
- :return: The absolute path where the resource is to be found.
631
- """
632
- r = self.get_distinfo_resource('RESOURCES')
633
- with contextlib.closing(r.as_stream()) as stream:
634
- with CSVReader(stream=stream) as resources_reader:
635
- for relative, destination in resources_reader:
636
- if relative == relative_path:
637
- return destination
638
- raise KeyError('no resource file with relative path %r '
639
- 'is installed' % relative_path)
640
-
641
- def list_installed_files(self):
642
- """
643
- Iterates over the ``RECORD`` entries and returns a tuple
644
- ``(path, hash, size)`` for each line.
645
-
646
- :returns: iterator of (path, hash, size)
647
- """
648
- for result in self._get_records():
649
- yield result
650
-
651
- def write_installed_files(self, paths, prefix, dry_run=False):
652
- """
653
- Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any
654
- existing ``RECORD`` file is silently overwritten.
655
-
656
- prefix is used to determine when to write absolute paths.
657
- """
658
- prefix = os.path.join(prefix, '')
659
- base = os.path.dirname(self.path)
660
- base_under_prefix = base.startswith(prefix)
661
- base = os.path.join(base, '')
662
- record_path = self.get_distinfo_file('RECORD')
663
- logger.info('creating %s', record_path)
664
- if dry_run:
665
- return None
666
- with CSVWriter(record_path) as writer:
667
- for path in paths:
668
- if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')):
669
- # do not put size and hash, as in PEP-376
670
- hash_value = size = ''
671
- else:
672
- size = '%d' % os.path.getsize(path)
673
- with open(path, 'rb') as fp:
674
- hash_value = self.get_hash(fp.read())
675
- if path.startswith(base) or (base_under_prefix and
676
- path.startswith(prefix)):
677
- path = os.path.relpath(path, base)
678
- writer.writerow((path, hash_value, size))
679
-
680
- # add the RECORD file itself
681
- if record_path.startswith(base):
682
- record_path = os.path.relpath(record_path, base)
683
- writer.writerow((record_path, '', ''))
684
- return record_path
685
-
686
- def check_installed_files(self):
687
- """
688
- Checks that the hashes and sizes of the files in ``RECORD`` are
689
- matched by the files themselves. Returns a (possibly empty) list of
690
- mismatches. Each entry in the mismatch list will be a tuple consisting
691
- of the path, 'exists', 'size' or 'hash' according to what didn't match
692
- (existence is checked first, then size, then hash), the expected
693
- value and the actual value.
694
- """
695
- mismatches = []
696
- base = os.path.dirname(self.path)
697
- record_path = self.get_distinfo_file('RECORD')
698
- for path, hash_value, size in self.list_installed_files():
699
- if not os.path.isabs(path):
700
- path = os.path.join(base, path)
701
- if path == record_path:
702
- continue
703
- if not os.path.exists(path):
704
- mismatches.append((path, 'exists', True, False))
705
- elif os.path.isfile(path):
706
- actual_size = str(os.path.getsize(path))
707
- if size and actual_size != size:
708
- mismatches.append((path, 'size', size, actual_size))
709
- elif hash_value:
710
- if '=' in hash_value:
711
- hasher = hash_value.split('=', 1)[0]
712
- else:
713
- hasher = None
714
-
715
- with open(path, 'rb') as f:
716
- actual_hash = self.get_hash(f.read(), hasher)
717
- if actual_hash != hash_value:
718
- mismatches.append((path, 'hash', hash_value, actual_hash))
719
- return mismatches
720
-
721
- @cached_property
722
- def shared_locations(self):
723
- """
724
- A dictionary of shared locations whose keys are in the set 'prefix',
725
- 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'.
726
- The corresponding value is the absolute path of that category for
727
- this distribution, and takes into account any paths selected by the
728
- user at installation time (e.g. via command-line arguments). In the
729
- case of the 'namespace' key, this would be a list of absolute paths
730
- for the roots of namespace packages in this distribution.
731
-
732
- The first time this property is accessed, the relevant information is
733
- read from the SHARED file in the .dist-info directory.
734
- """
735
- result = {}
736
- shared_path = os.path.join(self.path, 'SHARED')
737
- if os.path.isfile(shared_path):
738
- with codecs.open(shared_path, 'r', encoding='utf-8') as f:
739
- lines = f.read().splitlines()
740
- for line in lines:
741
- key, value = line.split('=', 1)
742
- if key == 'namespace':
743
- result.setdefault(key, []).append(value)
744
- else:
745
- result[key] = value
746
- return result
747
-
748
- def write_shared_locations(self, paths, dry_run=False):
749
- """
750
- Write shared location information to the SHARED file in .dist-info.
751
- :param paths: A dictionary as described in the documentation for
752
- :meth:`shared_locations`.
753
- :param dry_run: If True, the action is logged but no file is actually
754
- written.
755
- :return: The path of the file written to.
756
- """
757
- shared_path = os.path.join(self.path, 'SHARED')
758
- logger.info('creating %s', shared_path)
759
- if dry_run:
760
- return None
761
- lines = []
762
- for key in ('prefix', 'lib', 'headers', 'scripts', 'data'):
763
- path = paths[key]
764
- if os.path.isdir(paths[key]):
765
- lines.append('%s=%s' % (key, path))
766
- for ns in paths.get('namespace', ()):
767
- lines.append('namespace=%s' % ns)
768
-
769
- with codecs.open(shared_path, 'w', encoding='utf-8') as f:
770
- f.write('\n'.join(lines))
771
- return shared_path
772
-
773
- def get_distinfo_resource(self, path):
774
- if path not in DIST_FILES:
775
- raise DistlibException('invalid path for a dist-info file: '
776
- '%r at %r' % (path, self.path))
777
- finder = resources.finder_for_path(self.path)
778
- if finder is None:
779
- raise DistlibException('Unable to get a finder for %s' % self.path)
780
- return finder.find(path)
781
-
782
- def get_distinfo_file(self, path):
783
- """
784
- Returns a path located under the ``.dist-info`` directory. Returns a
785
- string representing the path.
786
-
787
- :parameter path: a ``'/'``-separated path relative to the
788
- ``.dist-info`` directory or an absolute path;
789
- If *path* is an absolute path and doesn't start
790
- with the ``.dist-info`` directory path,
791
- a :class:`DistlibException` is raised
792
- :type path: str
793
- :rtype: str
794
- """
795
- # Check if it is an absolute path # XXX use relpath, add tests
796
- if path.find(os.sep) >= 0:
797
- # it's an absolute path?
798
- distinfo_dirname, path = path.split(os.sep)[-2:]
799
- if distinfo_dirname != self.path.split(os.sep)[-1]:
800
- raise DistlibException(
801
- 'dist-info file %r does not belong to the %r %s '
802
- 'distribution' % (path, self.name, self.version))
803
-
804
- # The file must be relative
805
- if path not in DIST_FILES:
806
- raise DistlibException('invalid path for a dist-info file: '
807
- '%r at %r' % (path, self.path))
808
-
809
- return os.path.join(self.path, path)
810
-
811
- def list_distinfo_files(self):
812
- """
813
- Iterates over the ``RECORD`` entries and returns paths for each line if
814
- the path is pointing to a file located in the ``.dist-info`` directory
815
- or one of its subdirectories.
816
-
817
- :returns: iterator of paths
818
- """
819
- base = os.path.dirname(self.path)
820
- for path, checksum, size in self._get_records():
821
- # XXX add separator or use real relpath algo
822
- if not os.path.isabs(path):
823
- path = os.path.join(base, path)
824
- if path.startswith(self.path):
825
- yield path
826
-
827
- def __eq__(self, other):
828
- return (isinstance(other, InstalledDistribution) and
829
- self.path == other.path)
830
-
831
- # See http://docs.python.org/reference/datamodel#object.__hash__
832
- __hash__ = object.__hash__
833
-
834
-
835
- class EggInfoDistribution(BaseInstalledDistribution):
836
- """Created with the *path* of the ``.egg-info`` directory or file provided
837
- to the constructor. It reads the metadata contained in the file itself, or
838
- if the given path happens to be a directory, the metadata is read from the
839
- file ``PKG-INFO`` under that directory."""
840
-
841
- requested = True # as we have no way of knowing, assume it was
842
- shared_locations = {}
843
-
844
- def __init__(self, path, env=None):
845
- def set_name_and_version(s, n, v):
846
- s.name = n
847
- s.key = n.lower() # for case-insensitive comparisons
848
- s.version = v
849
-
850
- self.path = path
851
- self.dist_path = env
852
- if env and env._cache_enabled and path in env._cache_egg.path:
853
- metadata = env._cache_egg.path[path].metadata
854
- set_name_and_version(self, metadata.name, metadata.version)
855
- else:
856
- metadata = self._get_metadata(path)
857
-
858
- # Need to be set before caching
859
- set_name_and_version(self, metadata.name, metadata.version)
860
-
861
- if env and env._cache_enabled:
862
- env._cache_egg.add(self)
863
- super(EggInfoDistribution, self).__init__(metadata, path, env)
864
-
865
- def _get_metadata(self, path):
866
- requires = None
867
-
868
- def parse_requires_data(data):
869
- """Create a list of dependencies from a requires.txt file.
870
-
871
- *data*: the contents of a setuptools-produced requires.txt file.
872
- """
873
- reqs = []
874
- lines = data.splitlines()
875
- for line in lines:
876
- line = line.strip()
877
- if line.startswith('['):
878
- logger.warning('Unexpected line: quitting requirement scan: %r',
879
- line)
880
- break
881
- r = parse_requirement(line)
882
- if not r:
883
- logger.warning('Not recognised as a requirement: %r', line)
884
- continue
885
- if r.extras:
886
- logger.warning('extra requirements in requires.txt are '
887
- 'not supported')
888
- if not r.constraints:
889
- reqs.append(r.name)
890
- else:
891
- cons = ', '.join('%s%s' % c for c in r.constraints)
892
- reqs.append('%s (%s)' % (r.name, cons))
893
- return reqs
894
-
895
- def parse_requires_path(req_path):
896
- """Create a list of dependencies from a requires.txt file.
897
-
898
- *req_path*: the path to a setuptools-produced requires.txt file.
899
- """
900
-
901
- reqs = []
902
- try:
903
- with codecs.open(req_path, 'r', 'utf-8') as fp:
904
- reqs = parse_requires_data(fp.read())
905
- except IOError:
906
- pass
907
- return reqs
908
-
909
- if path.endswith('.egg'):
910
- if os.path.isdir(path):
911
- meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
912
- metadata = Metadata(path=meta_path, scheme='legacy')
913
- req_path = os.path.join(path, 'EGG-INFO', 'requires.txt')
914
- requires = parse_requires_path(req_path)
915
- else:
916
- # FIXME handle the case where zipfile is not available
917
- zipf = zipimport.zipimporter(path)
918
- fileobj = StringIO(
919
- zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'))
920
- metadata = Metadata(fileobj=fileobj, scheme='legacy')
921
- try:
922
- data = zipf.get_data('EGG-INFO/requires.txt')
923
- requires = parse_requires_data(data.decode('utf-8'))
924
- except IOError:
925
- requires = None
926
- elif path.endswith('.egg-info'):
927
- if os.path.isdir(path):
928
- path = os.path.join(path, 'PKG-INFO')
929
- req_path = os.path.join(path, 'requires.txt')
930
- requires = parse_requires_path(req_path)
931
- metadata = Metadata(path=path, scheme='legacy')
932
- else:
933
- raise DistlibException('path must end with .egg-info or .egg, '
934
- 'got %r' % path)
935
-
936
- if requires:
937
- metadata.add_requirements(requires)
938
- return metadata
939
-
940
- def __repr__(self):
941
- return '<EggInfoDistribution %r %s at %r>' % (
942
- self.name, self.version, self.path)
943
-
944
- def __str__(self):
945
- return "%s %s" % (self.name, self.version)
946
-
947
- def check_installed_files(self):
948
- """
949
- Checks that the hashes and sizes of the files in ``RECORD`` are
950
- matched by the files themselves. Returns a (possibly empty) list of
951
- mismatches. Each entry in the mismatch list will be a tuple consisting
952
- of the path, 'exists', 'size' or 'hash' according to what didn't match
953
- (existence is checked first, then size, then hash), the expected
954
- value and the actual value.
955
- """
956
- mismatches = []
957
- record_path = os.path.join(self.path, 'installed-files.txt')
958
- if os.path.exists(record_path):
959
- for path, _, _ in self.list_installed_files():
960
- if path == record_path:
961
- continue
962
- if not os.path.exists(path):
963
- mismatches.append((path, 'exists', True, False))
964
- return mismatches
965
-
966
- def list_installed_files(self):
967
- """
968
- Iterates over the ``installed-files.txt`` entries and returns a tuple
969
- ``(path, hash, size)`` for each line.
970
-
971
- :returns: a list of (path, hash, size)
972
- """
973
-
974
- def _md5(path):
975
- f = open(path, 'rb')
976
- try:
977
- content = f.read()
978
- finally:
979
- f.close()
980
- return hashlib.md5(content).hexdigest()
981
-
982
- def _size(path):
983
- return os.stat(path).st_size
984
-
985
- record_path = os.path.join(self.path, 'installed-files.txt')
986
- result = []
987
- if os.path.exists(record_path):
988
- with codecs.open(record_path, 'r', encoding='utf-8') as f:
989
- for line in f:
990
- line = line.strip()
991
- p = os.path.normpath(os.path.join(self.path, line))
992
- # "./" is present as a marker between installed files
993
- # and installation metadata files
994
- if not os.path.exists(p):
995
- logger.warning('Non-existent file: %s', p)
996
- if p.endswith(('.pyc', '.pyo')):
997
- continue
998
- #otherwise fall through and fail
999
- if not os.path.isdir(p):
1000
- result.append((p, _md5(p), _size(p)))
1001
- result.append((record_path, None, None))
1002
- return result
1003
-
1004
- def list_distinfo_files(self, absolute=False):
1005
- """
1006
- Iterates over the ``installed-files.txt`` entries and returns paths for
1007
- each line if the path is pointing to a file located in the
1008
- ``.egg-info`` directory or one of its subdirectories.
1009
-
1010
- :parameter absolute: If *absolute* is ``True``, each returned path is
1011
- transformed into a local absolute path. Otherwise the
1012
- raw value from ``installed-files.txt`` is returned.
1013
- :type absolute: boolean
1014
- :returns: iterator of paths
1015
- """
1016
- record_path = os.path.join(self.path, 'installed-files.txt')
1017
- skip = True
1018
- with codecs.open(record_path, 'r', encoding='utf-8') as f:
1019
- for line in f:
1020
- line = line.strip()
1021
- if line == './':
1022
- skip = False
1023
- continue
1024
- if not skip:
1025
- p = os.path.normpath(os.path.join(self.path, line))
1026
- if p.startswith(self.path):
1027
- if absolute:
1028
- yield p
1029
- else:
1030
- yield line
1031
-
1032
- def __eq__(self, other):
1033
- return (isinstance(other, EggInfoDistribution) and
1034
- self.path == other.path)
1035
-
1036
- # See http://docs.python.org/reference/datamodel#object.__hash__
1037
- __hash__ = object.__hash__
1038
-
1039
- new_dist_class = InstalledDistribution
1040
- old_dist_class = EggInfoDistribution
1041
-
1042
-
1043
- class DependencyGraph(object):
1044
- """
1045
- Represents a dependency graph between distributions.
1046
-
1047
- The dependency relationships are stored in an ``adjacency_list`` that maps
1048
- distributions to a list of ``(other, label)`` tuples where ``other``
1049
- is a distribution and the edge is labeled with ``label`` (i.e. the version
1050
- specifier, if such was provided). Also, for more efficient traversal, for
1051
- every distribution ``x``, a list of predecessors is kept in
1052
- ``reverse_list[x]``. An edge from distribution ``a`` to
1053
- distribution ``b`` means that ``a`` depends on ``b``. If any missing
1054
- dependencies are found, they are stored in ``missing``, which is a
1055
- dictionary that maps distributions to a list of requirements that were not
1056
- provided by any other distributions.
1057
- """
1058
-
1059
- def __init__(self):
1060
- self.adjacency_list = {}
1061
- self.reverse_list = {}
1062
- self.missing = {}
1063
-
1064
- def add_distribution(self, distribution):
1065
- """Add the *distribution* to the graph.
1066
-
1067
- :type distribution: :class:`distutils2.database.InstalledDistribution`
1068
- or :class:`distutils2.database.EggInfoDistribution`
1069
- """
1070
- self.adjacency_list[distribution] = []
1071
- self.reverse_list[distribution] = []
1072
- #self.missing[distribution] = []
1073
-
1074
- def add_edge(self, x, y, label=None):
1075
- """Add an edge from distribution *x* to distribution *y* with the given
1076
- *label*.
1077
-
1078
- :type x: :class:`distutils2.database.InstalledDistribution` or
1079
- :class:`distutils2.database.EggInfoDistribution`
1080
- :type y: :class:`distutils2.database.InstalledDistribution` or
1081
- :class:`distutils2.database.EggInfoDistribution`
1082
- :type label: ``str`` or ``None``
1083
- """
1084
- self.adjacency_list[x].append((y, label))
1085
- # multiple edges are allowed, so be careful
1086
- if x not in self.reverse_list[y]:
1087
- self.reverse_list[y].append(x)
1088
-
1089
- def add_missing(self, distribution, requirement):
1090
- """
1091
- Add a missing *requirement* for the given *distribution*.
1092
-
1093
- :type distribution: :class:`distutils2.database.InstalledDistribution`
1094
- or :class:`distutils2.database.EggInfoDistribution`
1095
- :type requirement: ``str``
1096
- """
1097
- logger.debug('%s missing %r', distribution, requirement)
1098
- self.missing.setdefault(distribution, []).append(requirement)
1099
-
1100
- def _repr_dist(self, dist):
1101
- return '%s %s' % (dist.name, dist.version)
1102
-
1103
- def repr_node(self, dist, level=1):
1104
- """Prints only a subgraph"""
1105
- output = [self._repr_dist(dist)]
1106
- for other, label in self.adjacency_list[dist]:
1107
- dist = self._repr_dist(other)
1108
- if label is not None:
1109
- dist = '%s [%s]' % (dist, label)
1110
- output.append(' ' * level + str(dist))
1111
- suboutput = self.repr_node(other, level + 1)
1112
- subs = suboutput.split('\n')
1113
- output.extend(subs[1:])
1114
- return '\n'.join(output)
1115
-
1116
- def to_dot(self, f, skip_disconnected=True):
1117
- """Writes a DOT output for the graph to the provided file *f*.
1118
-
1119
- If *skip_disconnected* is set to ``True``, then all distributions
1120
- that are not dependent on any other distribution are skipped.
1121
-
1122
- :type f: has to support ``file``-like operations
1123
- :type skip_disconnected: ``bool``
1124
- """
1125
- disconnected = []
1126
-
1127
- f.write("digraph dependencies {\n")
1128
- for dist, adjs in self.adjacency_list.items():
1129
- if len(adjs) == 0 and not skip_disconnected:
1130
- disconnected.append(dist)
1131
- for other, label in adjs:
1132
- if not label is None:
1133
- f.write('"%s" -> "%s" [label="%s"]\n' %
1134
- (dist.name, other.name, label))
1135
- else:
1136
- f.write('"%s" -> "%s"\n' % (dist.name, other.name))
1137
- if not skip_disconnected and len(disconnected) > 0:
1138
- f.write('subgraph disconnected {\n')
1139
- f.write('label = "Disconnected"\n')
1140
- f.write('bgcolor = red\n')
1141
-
1142
- for dist in disconnected:
1143
- f.write('"%s"' % dist.name)
1144
- f.write('\n')
1145
- f.write('}\n')
1146
- f.write('}\n')
1147
-
1148
- def topological_sort(self):
1149
- """
1150
- Perform a topological sort of the graph.
1151
- :return: A tuple, the first element of which is a topologically sorted
1152
- list of distributions, and the second element of which is a
1153
- list of distributions that cannot be sorted because they have
1154
- circular dependencies and so form a cycle.
1155
- """
1156
- result = []
1157
- # Make a shallow copy of the adjacency list
1158
- alist = {}
1159
- for k, v in self.adjacency_list.items():
1160
- alist[k] = v[:]
1161
- while True:
1162
- # See what we can remove in this run
1163
- to_remove = []
1164
- for k, v in list(alist.items())[:]:
1165
- if not v:
1166
- to_remove.append(k)
1167
- del alist[k]
1168
- if not to_remove:
1169
- # What's left in alist (if anything) is a cycle.
1170
- break
1171
- # Remove from the adjacency list of others
1172
- for k, v in alist.items():
1173
- alist[k] = [(d, r) for d, r in v if d not in to_remove]
1174
- logger.debug('Moving to result: %s',
1175
- ['%s (%s)' % (d.name, d.version) for d in to_remove])
1176
- result.extend(to_remove)
1177
- return result, list(alist.keys())
1178
-
1179
- def __repr__(self):
1180
- """Representation of the graph"""
1181
- output = []
1182
- for dist, adjs in self.adjacency_list.items():
1183
- output.append(self.repr_node(dist))
1184
- return '\n'.join(output)
1185
-
1186
-
1187
- def make_graph(dists, scheme='default'):
1188
- """Makes a dependency graph from the given distributions.
1189
-
1190
- :parameter dists: a list of distributions
1191
- :type dists: list of :class:`distutils2.database.InstalledDistribution` and
1192
- :class:`distutils2.database.EggInfoDistribution` instances
1193
- :rtype: a :class:`DependencyGraph` instance
1194
- """
1195
- scheme = get_scheme(scheme)
1196
- graph = DependencyGraph()
1197
- provided = {} # maps names to lists of (version, dist) tuples
1198
-
1199
- # first, build the graph and find out what's provided
1200
- for dist in dists:
1201
- graph.add_distribution(dist)
1202
-
1203
- for p in dist.provides:
1204
- name, version = parse_name_and_version(p)
1205
- logger.debug('Add to provided: %s, %s, %s', name, version, dist)
1206
- provided.setdefault(name, []).append((version, dist))
1207
-
1208
- # now make the edges
1209
- for dist in dists:
1210
- requires = (dist.run_requires | dist.meta_requires |
1211
- dist.build_requires | dist.dev_requires)
1212
- for req in requires:
1213
- try:
1214
- matcher = scheme.matcher(req)
1215
- except UnsupportedVersionError:
1216
- # XXX compat-mode if cannot read the version
1217
- logger.warning('could not read version %r - using name only',
1218
- req)
1219
- name = req.split()[0]
1220
- matcher = scheme.matcher(name)
1221
-
1222
- name = matcher.key # case-insensitive
1223
-
1224
- matched = False
1225
- if name in provided:
1226
- for version, provider in provided[name]:
1227
- try:
1228
- match = matcher.match(version)
1229
- except UnsupportedVersionError:
1230
- match = False
1231
-
1232
- if match:
1233
- graph.add_edge(dist, provider, req)
1234
- matched = True
1235
- break
1236
- if not matched:
1237
- graph.add_missing(dist, req)
1238
- return graph
1239
-
1240
-
1241
- def get_dependent_dists(dists, dist):
1242
- """Recursively generate a list of distributions from *dists* that are
1243
- dependent on *dist*.
1244
-
1245
- :param dists: a list of distributions
1246
- :param dist: a distribution, member of *dists* for which we are interested
1247
- """
1248
- if dist not in dists:
1249
- raise DistlibException('given distribution %r is not a member '
1250
- 'of the list' % dist.name)
1251
- graph = make_graph(dists)
1252
-
1253
- dep = [dist] # dependent distributions
1254
- todo = graph.reverse_list[dist] # list of nodes we should inspect
1255
-
1256
- while todo:
1257
- d = todo.pop()
1258
- dep.append(d)
1259
- for succ in graph.reverse_list[d]:
1260
- if succ not in dep:
1261
- todo.append(succ)
1262
-
1263
- dep.pop(0) # remove dist from dep, was there to prevent infinite loops
1264
- return dep
1265
-
1266
-
1267
- def get_required_dists(dists, dist):
1268
- """Recursively generate a list of distributions from *dists* that are
1269
- required by *dist*.
1270
-
1271
- :param dists: a list of distributions
1272
- :param dist: a distribution, member of *dists* for which we are interested
1273
- """
1274
- if dist not in dists:
1275
- raise DistlibException('given distribution %r is not a member '
1276
- 'of the list' % dist.name)
1277
- graph = make_graph(dists)
1278
-
1279
- req = [] # required distributions
1280
- todo = graph.adjacency_list[dist] # list of nodes we should inspect
1281
-
1282
- while todo:
1283
- d = todo.pop()[0]
1284
- req.append(d)
1285
- for pred in graph.adjacency_list[d]:
1286
- if pred not in req:
1287
- todo.append(pred)
1288
-
1289
- return req
1290
-
1291
-
1292
- def make_dist(name, version, **kwargs):
1293
- """
1294
- A convenience method for making a dist given just a name and version.
1295
- """
1296
- summary = kwargs.pop('summary', 'Placeholder for summary')
1297
- md = Metadata(**kwargs)
1298
- md.name = name
1299
- md.version = version
1300
- md.summary = summary or 'Plaeholder for summary'
1301
- return Distribution(md)
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012-2017 The Python Software Foundation.
4
+ # See LICENSE.txt and CONTRIBUTORS.txt.
5
+ #
6
+ """PEP 376 implementation."""
7
+
8
+ from __future__ import unicode_literals
9
+
10
+ import base64
11
+ import codecs
12
+ import contextlib
13
+ import hashlib
14
+ import logging
15
+ import os
16
+ import posixpath
17
+ import sys
18
+ import zipimport
19
+
20
+ from . import DistlibException, resources
21
+ from .compat import StringIO
22
+ from .version import get_scheme, UnsupportedVersionError
23
+ from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME
24
+ from .util import (parse_requirement, cached_property, parse_name_and_version,
25
+ read_exports, write_exports, CSVReader, CSVWriter)
26
+
27
+
28
+ __all__ = ['Distribution', 'BaseInstalledDistribution',
29
+ 'InstalledDistribution', 'EggInfoDistribution',
30
+ 'DistributionPath']
31
+
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ EXPORTS_FILENAME = 'pydist-exports.json'
36
+ COMMANDS_FILENAME = 'pydist-commands.json'
37
+
38
+ DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED',
39
+ 'RESOURCES', EXPORTS_FILENAME, 'SHARED')
40
+
41
+ DISTINFO_EXT = '.dist-info'
42
+
43
+
44
+ class _Cache(object):
45
+ """
46
+ A simple cache mapping names and .dist-info paths to distributions
47
+ """
48
+ def __init__(self):
49
+ """
50
+ Initialise an instance. There is normally one for each DistributionPath.
51
+ """
52
+ self.name = {}
53
+ self.path = {}
54
+ self.generated = False
55
+
56
+ def clear(self):
57
+ """
58
+ Clear the cache, setting it to its initial state.
59
+ """
60
+ self.name.clear()
61
+ self.path.clear()
62
+ self.generated = False
63
+
64
+ def add(self, dist):
65
+ """
66
+ Add a distribution to the cache.
67
+ :param dist: The distribution to add.
68
+ """
69
+ if dist.path not in self.path:
70
+ self.path[dist.path] = dist
71
+ self.name.setdefault(dist.key, []).append(dist)
72
+
73
+
74
+ class DistributionPath(object):
75
+ """
76
+ Represents a set of distributions installed on a path (typically sys.path).
77
+ """
78
+ def __init__(self, path=None, include_egg=False):
79
+ """
80
+ Create an instance from a path, optionally including legacy (distutils/
81
+ setuptools/distribute) distributions.
82
+ :param path: The path to use, as a list of directories. If not specified,
83
+ sys.path is used.
84
+ :param include_egg: If True, this instance will look for and return legacy
85
+ distributions as well as those based on PEP 376.
86
+ """
87
+ if path is None:
88
+ path = sys.path
89
+ self.path = path
90
+ self._include_dist = True
91
+ self._include_egg = include_egg
92
+
93
+ self._cache = _Cache()
94
+ self._cache_egg = _Cache()
95
+ self._cache_enabled = True
96
+ self._scheme = get_scheme('default')
97
+
98
+ def _get_cache_enabled(self):
99
+ return self._cache_enabled
100
+
101
+ def _set_cache_enabled(self, value):
102
+ self._cache_enabled = value
103
+
104
+ cache_enabled = property(_get_cache_enabled, _set_cache_enabled)
105
+
106
+ def clear_cache(self):
107
+ """
108
+ Clears the internal cache.
109
+ """
110
+ self._cache.clear()
111
+ self._cache_egg.clear()
112
+
113
+
114
+ def _yield_distributions(self):
115
+ """
116
+ Yield .dist-info and/or .egg(-info) distributions.
117
+ """
118
+ # We need to check if we've seen some resources already, because on
119
+ # some Linux systems (e.g. some Debian/Ubuntu variants) there are
120
+ # symlinks which alias other files in the environment.
121
+ seen = set()
122
+ for path in self.path:
123
+ finder = resources.finder_for_path(path)
124
+ if finder is None:
125
+ continue
126
+ r = finder.find('')
127
+ if not r or not r.is_container:
128
+ continue
129
+ rset = sorted(r.resources)
130
+ for entry in rset:
131
+ r = finder.find(entry)
132
+ if not r or r.path in seen:
133
+ continue
134
+ if self._include_dist and entry.endswith(DISTINFO_EXT):
135
+ possible_filenames = [METADATA_FILENAME, WHEEL_METADATA_FILENAME]
136
+ for metadata_filename in possible_filenames:
137
+ metadata_path = posixpath.join(entry, metadata_filename)
138
+ pydist = finder.find(metadata_path)
139
+ if pydist:
140
+ break
141
+ else:
142
+ continue
143
+
144
+ with contextlib.closing(pydist.as_stream()) as stream:
145
+ metadata = Metadata(fileobj=stream, scheme='legacy')
146
+ logger.debug('Found %s', r.path)
147
+ seen.add(r.path)
148
+ yield new_dist_class(r.path, metadata=metadata,
149
+ env=self)
150
+ elif self._include_egg and entry.endswith(('.egg-info',
151
+ '.egg')):
152
+ logger.debug('Found %s', r.path)
153
+ seen.add(r.path)
154
+ yield old_dist_class(r.path, self)
155
+
156
+ def _generate_cache(self):
157
+ """
158
+ Scan the path for distributions and populate the cache with
159
+ those that are found.
160
+ """
161
+ gen_dist = not self._cache.generated
162
+ gen_egg = self._include_egg and not self._cache_egg.generated
163
+ if gen_dist or gen_egg:
164
+ for dist in self._yield_distributions():
165
+ if isinstance(dist, InstalledDistribution):
166
+ self._cache.add(dist)
167
+ else:
168
+ self._cache_egg.add(dist)
169
+
170
+ if gen_dist:
171
+ self._cache.generated = True
172
+ if gen_egg:
173
+ self._cache_egg.generated = True
174
+
175
+ @classmethod
176
+ def distinfo_dirname(cls, name, version):
177
+ """
178
+ The *name* and *version* parameters are converted into their
179
+ filename-escaped form, i.e. any ``'-'`` characters are replaced
180
+ with ``'_'`` other than the one in ``'dist-info'`` and the one
181
+ separating the name from the version number.
182
+
183
+ :parameter name: is converted to a standard distribution name by replacing
184
+ any runs of non- alphanumeric characters with a single
185
+ ``'-'``.
186
+ :type name: string
187
+ :parameter version: is converted to a standard version string. Spaces
188
+ become dots, and all other non-alphanumeric characters
189
+ (except dots) become dashes, with runs of multiple
190
+ dashes condensed to a single dash.
191
+ :type version: string
192
+ :returns: directory name
193
+ :rtype: string"""
194
+ name = name.replace('-', '_')
195
+ return '-'.join([name, version]) + DISTINFO_EXT
196
+
197
+ def get_distributions(self):
198
+ """
199
+ Provides an iterator that looks for distributions and returns
200
+ :class:`InstalledDistribution` or
201
+ :class:`EggInfoDistribution` instances for each one of them.
202
+
203
+ :rtype: iterator of :class:`InstalledDistribution` and
204
+ :class:`EggInfoDistribution` instances
205
+ """
206
+ if not self._cache_enabled:
207
+ for dist in self._yield_distributions():
208
+ yield dist
209
+ else:
210
+ self._generate_cache()
211
+
212
+ for dist in self._cache.path.values():
213
+ yield dist
214
+
215
+ if self._include_egg:
216
+ for dist in self._cache_egg.path.values():
217
+ yield dist
218
+
219
+ def get_distribution(self, name):
220
+ """
221
+ Looks for a named distribution on the path.
222
+
223
+ This function only returns the first result found, as no more than one
224
+ value is expected. If nothing is found, ``None`` is returned.
225
+
226
+ :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution`
227
+ or ``None``
228
+ """
229
+ result = None
230
+ name = name.lower()
231
+ if not self._cache_enabled:
232
+ for dist in self._yield_distributions():
233
+ if dist.key == name:
234
+ result = dist
235
+ break
236
+ else:
237
+ self._generate_cache()
238
+
239
+ if name in self._cache.name:
240
+ result = self._cache.name[name][0]
241
+ elif self._include_egg and name in self._cache_egg.name:
242
+ result = self._cache_egg.name[name][0]
243
+ return result
244
+
245
+ def provides_distribution(self, name, version=None):
246
+ """
247
+ Iterates over all distributions to find which distributions provide *name*.
248
+ If a *version* is provided, it will be used to filter the results.
249
+
250
+ This function only returns the first result found, since no more than
251
+ one values are expected. If the directory is not found, returns ``None``.
252
+
253
+ :parameter version: a version specifier that indicates the version
254
+ required, conforming to the format in ``PEP-345``
255
+
256
+ :type name: string
257
+ :type version: string
258
+ """
259
+ matcher = None
260
+ if version is not None:
261
+ try:
262
+ matcher = self._scheme.matcher('%s (%s)' % (name, version))
263
+ except ValueError:
264
+ raise DistlibException('invalid name or version: %r, %r' %
265
+ (name, version))
266
+
267
+ for dist in self.get_distributions():
268
+ # We hit a problem on Travis where enum34 was installed and doesn't
269
+ # have a provides attribute ...
270
+ if not hasattr(dist, 'provides'):
271
+ logger.debug('No "provides": %s', dist)
272
+ else:
273
+ provided = dist.provides
274
+
275
+ for p in provided:
276
+ p_name, p_ver = parse_name_and_version(p)
277
+ if matcher is None:
278
+ if p_name == name:
279
+ yield dist
280
+ break
281
+ else:
282
+ if p_name == name and matcher.match(p_ver):
283
+ yield dist
284
+ break
285
+
286
+ def get_file_path(self, name, relative_path):
287
+ """
288
+ Return the path to a resource file.
289
+ """
290
+ dist = self.get_distribution(name)
291
+ if dist is None:
292
+ raise LookupError('no distribution named %r found' % name)
293
+ return dist.get_resource_path(relative_path)
294
+
295
+ def get_exported_entries(self, category, name=None):
296
+ """
297
+ Return all of the exported entries in a particular category.
298
+
299
+ :param category: The category to search for entries.
300
+ :param name: If specified, only entries with that name are returned.
301
+ """
302
+ for dist in self.get_distributions():
303
+ r = dist.exports
304
+ if category in r:
305
+ d = r[category]
306
+ if name is not None:
307
+ if name in d:
308
+ yield d[name]
309
+ else:
310
+ for v in d.values():
311
+ yield v
312
+
313
+
314
+ class Distribution(object):
315
+ """
316
+ A base class for distributions, whether installed or from indexes.
317
+ Either way, it must have some metadata, so that's all that's needed
318
+ for construction.
319
+ """
320
+
321
+ build_time_dependency = False
322
+ """
323
+ Set to True if it's known to be only a build-time dependency (i.e.
324
+ not needed after installation).
325
+ """
326
+
327
+ requested = False
328
+ """A boolean that indicates whether the ``REQUESTED`` metadata file is
329
+ present (in other words, whether the package was installed by user
330
+ request or it was installed as a dependency)."""
331
+
332
+ def __init__(self, metadata):
333
+ """
334
+ Initialise an instance.
335
+ :param metadata: The instance of :class:`Metadata` describing this
336
+ distribution.
337
+ """
338
+ self.metadata = metadata
339
+ self.name = metadata.name
340
+ self.key = self.name.lower() # for case-insensitive comparisons
341
+ self.version = metadata.version
342
+ self.locator = None
343
+ self.digest = None
344
+ self.extras = None # additional features requested
345
+ self.context = None # environment marker overrides
346
+ self.download_urls = set()
347
+ self.digests = {}
348
+
349
+ @property
350
+ def source_url(self):
351
+ """
352
+ The source archive download URL for this distribution.
353
+ """
354
+ return self.metadata.source_url
355
+
356
+ download_url = source_url # Backward compatibility
357
+
358
+ @property
359
+ def name_and_version(self):
360
+ """
361
+ A utility property which displays the name and version in parentheses.
362
+ """
363
+ return '%s (%s)' % (self.name, self.version)
364
+
365
+ @property
366
+ def provides(self):
367
+ """
368
+ A set of distribution names and versions provided by this distribution.
369
+ :return: A set of "name (version)" strings.
370
+ """
371
+ plist = self.metadata.provides
372
+ s = '%s (%s)' % (self.name, self.version)
373
+ if s not in plist:
374
+ plist.append(s)
375
+ return plist
376
+
377
+ def _get_requirements(self, req_attr):
378
+ md = self.metadata
379
+ logger.debug('Getting requirements from metadata %r', md.todict())
380
+ reqts = getattr(md, req_attr)
381
+ return set(md.get_requirements(reqts, extras=self.extras,
382
+ env=self.context))
383
+
384
+ @property
385
+ def run_requires(self):
386
+ return self._get_requirements('run_requires')
387
+
388
+ @property
389
+ def meta_requires(self):
390
+ return self._get_requirements('meta_requires')
391
+
392
+ @property
393
+ def build_requires(self):
394
+ return self._get_requirements('build_requires')
395
+
396
+ @property
397
+ def test_requires(self):
398
+ return self._get_requirements('test_requires')
399
+
400
+ @property
401
+ def dev_requires(self):
402
+ return self._get_requirements('dev_requires')
403
+
404
+ def matches_requirement(self, req):
405
+ """
406
+ Say if this instance matches (fulfills) a requirement.
407
+ :param req: The requirement to match.
408
+ :rtype req: str
409
+ :return: True if it matches, else False.
410
+ """
411
+ # Requirement may contain extras - parse to lose those
412
+ # from what's passed to the matcher
413
+ r = parse_requirement(req)
414
+ scheme = get_scheme(self.metadata.scheme)
415
+ try:
416
+ matcher = scheme.matcher(r.requirement)
417
+ except UnsupportedVersionError:
418
+ # XXX compat-mode if cannot read the version
419
+ logger.warning('could not read version %r - using name only',
420
+ req)
421
+ name = req.split()[0]
422
+ matcher = scheme.matcher(name)
423
+
424
+ name = matcher.key # case-insensitive
425
+
426
+ result = False
427
+ for p in self.provides:
428
+ p_name, p_ver = parse_name_and_version(p)
429
+ if p_name != name:
430
+ continue
431
+ try:
432
+ result = matcher.match(p_ver)
433
+ break
434
+ except UnsupportedVersionError:
435
+ pass
436
+ return result
437
+
438
+ def __repr__(self):
439
+ """
440
+ Return a textual representation of this instance,
441
+ """
442
+ if self.source_url:
443
+ suffix = ' [%s]' % self.source_url
444
+ else:
445
+ suffix = ''
446
+ return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix)
447
+
448
+ def __eq__(self, other):
449
+ """
450
+ See if this distribution is the same as another.
451
+ :param other: The distribution to compare with. To be equal to one
452
+ another. distributions must have the same type, name,
453
+ version and source_url.
454
+ :return: True if it is the same, else False.
455
+ """
456
+ if type(other) is not type(self):
457
+ result = False
458
+ else:
459
+ result = (self.name == other.name and
460
+ self.version == other.version and
461
+ self.source_url == other.source_url)
462
+ return result
463
+
464
+ def __hash__(self):
465
+ """
466
+ Compute hash in a way which matches the equality test.
467
+ """
468
+ return hash(self.name) + hash(self.version) + hash(self.source_url)
469
+
470
+
471
+ class BaseInstalledDistribution(Distribution):
472
+ """
473
+ This is the base class for installed distributions (whether PEP 376 or
474
+ legacy).
475
+ """
476
+
477
+ hasher = None
478
+
479
+ def __init__(self, metadata, path, env=None):
480
+ """
481
+ Initialise an instance.
482
+ :param metadata: An instance of :class:`Metadata` which describes the
483
+ distribution. This will normally have been initialised
484
+ from a metadata file in the ``path``.
485
+ :param path: The path of the ``.dist-info`` or ``.egg-info``
486
+ directory for the distribution.
487
+ :param env: This is normally the :class:`DistributionPath`
488
+ instance where this distribution was found.
489
+ """
490
+ super(BaseInstalledDistribution, self).__init__(metadata)
491
+ self.path = path
492
+ self.dist_path = env
493
+
494
+ def get_hash(self, data, hasher=None):
495
+ """
496
+ Get the hash of some data, using a particular hash algorithm, if
497
+ specified.
498
+
499
+ :param data: The data to be hashed.
500
+ :type data: bytes
501
+ :param hasher: The name of a hash implementation, supported by hashlib,
502
+ or ``None``. Examples of valid values are ``'sha1'``,
503
+ ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and
504
+ ``'sha512'``. If no hasher is specified, the ``hasher``
505
+ attribute of the :class:`InstalledDistribution` instance
506
+ is used. If the hasher is determined to be ``None``, MD5
507
+ is used as the hashing algorithm.
508
+ :returns: The hash of the data. If a hasher was explicitly specified,
509
+ the returned hash will be prefixed with the specified hasher
510
+ followed by '='.
511
+ :rtype: str
512
+ """
513
+ if hasher is None:
514
+ hasher = self.hasher
515
+ if hasher is None:
516
+ hasher = hashlib.md5
517
+ prefix = ''
518
+ else:
519
+ hasher = getattr(hashlib, hasher)
520
+ prefix = '%s=' % self.hasher
521
+ digest = hasher(data).digest()
522
+ digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii')
523
+ return '%s%s' % (prefix, digest)
524
+
525
+
526
+ class InstalledDistribution(BaseInstalledDistribution):
527
+ """
528
+ Created with the *path* of the ``.dist-info`` directory provided to the
529
+ constructor. It reads the metadata contained in ``pydist.json`` when it is
530
+ instantiated., or uses a passed in Metadata instance (useful for when
531
+ dry-run mode is being used).
532
+ """
533
+
534
+ hasher = 'sha256'
535
+
536
+ def __init__(self, path, metadata=None, env=None):
537
+ self.finder = finder = resources.finder_for_path(path)
538
+ if finder is None:
539
+ import pdb; pdb.set_trace ()
540
+ if env and env._cache_enabled and path in env._cache.path:
541
+ metadata = env._cache.path[path].metadata
542
+ elif metadata is None:
543
+ r = finder.find(METADATA_FILENAME)
544
+ # Temporary - for Wheel 0.23 support
545
+ if r is None:
546
+ r = finder.find(WHEEL_METADATA_FILENAME)
547
+ # Temporary - for legacy support
548
+ if r is None:
549
+ r = finder.find('METADATA')
550
+ if r is None:
551
+ raise ValueError('no %s found in %s' % (METADATA_FILENAME,
552
+ path))
553
+ with contextlib.closing(r.as_stream()) as stream:
554
+ metadata = Metadata(fileobj=stream, scheme='legacy')
555
+
556
+ super(InstalledDistribution, self).__init__(metadata, path, env)
557
+
558
+ if env and env._cache_enabled:
559
+ env._cache.add(self)
560
+
561
+ try:
562
+ r = finder.find('REQUESTED')
563
+ except AttributeError:
564
+ import pdb; pdb.set_trace ()
565
+ self.requested = r is not None
566
+
567
+ def __repr__(self):
568
+ return '<InstalledDistribution %r %s at %r>' % (
569
+ self.name, self.version, self.path)
570
+
571
+ def __str__(self):
572
+ return "%s %s" % (self.name, self.version)
573
+
574
+ def _get_records(self):
575
+ """
576
+ Get the list of installed files for the distribution
577
+ :return: A list of tuples of path, hash and size. Note that hash and
578
+ size might be ``None`` for some entries. The path is exactly
579
+ as stored in the file (which is as in PEP 376).
580
+ """
581
+ results = []
582
+ r = self.get_distinfo_resource('RECORD')
583
+ with contextlib.closing(r.as_stream()) as stream:
584
+ with CSVReader(stream=stream) as record_reader:
585
+ # Base location is parent dir of .dist-info dir
586
+ #base_location = os.path.dirname(self.path)
587
+ #base_location = os.path.abspath(base_location)
588
+ for row in record_reader:
589
+ missing = [None for i in range(len(row), 3)]
590
+ path, checksum, size = row + missing
591
+ #if not os.path.isabs(path):
592
+ # path = path.replace('/', os.sep)
593
+ # path = os.path.join(base_location, path)
594
+ results.append((path, checksum, size))
595
+ return results
596
+
597
+ @cached_property
598
+ def exports(self):
599
+ """
600
+ Return the information exported by this distribution.
601
+ :return: A dictionary of exports, mapping an export category to a dict
602
+ of :class:`ExportEntry` instances describing the individual
603
+ export entries, and keyed by name.
604
+ """
605
+ result = {}
606
+ r = self.get_distinfo_resource(EXPORTS_FILENAME)
607
+ if r:
608
+ result = self.read_exports()
609
+ return result
610
+
611
+ def read_exports(self):
612
+ """
613
+ Read exports data from a file in .ini format.
614
+
615
+ :return: A dictionary of exports, mapping an export category to a list
616
+ of :class:`ExportEntry` instances describing the individual
617
+ export entries.
618
+ """
619
+ result = {}
620
+ r = self.get_distinfo_resource(EXPORTS_FILENAME)
621
+ if r:
622
+ with contextlib.closing(r.as_stream()) as stream:
623
+ result = read_exports(stream)
624
+ return result
625
+
626
+ def write_exports(self, exports):
627
+ """
628
+ Write a dictionary of exports to a file in .ini format.
629
+ :param exports: A dictionary of exports, mapping an export category to
630
+ a list of :class:`ExportEntry` instances describing the
631
+ individual export entries.
632
+ """
633
+ rf = self.get_distinfo_file(EXPORTS_FILENAME)
634
+ with open(rf, 'w') as f:
635
+ write_exports(exports, f)
636
+
637
+ def get_resource_path(self, relative_path):
638
+ """
639
+ NOTE: This API may change in the future.
640
+
641
+ Return the absolute path to a resource file with the given relative
642
+ path.
643
+
644
+ :param relative_path: The path, relative to .dist-info, of the resource
645
+ of interest.
646
+ :return: The absolute path where the resource is to be found.
647
+ """
648
+ r = self.get_distinfo_resource('RESOURCES')
649
+ with contextlib.closing(r.as_stream()) as stream:
650
+ with CSVReader(stream=stream) as resources_reader:
651
+ for relative, destination in resources_reader:
652
+ if relative == relative_path:
653
+ return destination
654
+ raise KeyError('no resource file with relative path %r '
655
+ 'is installed' % relative_path)
656
+
657
+ def list_installed_files(self):
658
+ """
659
+ Iterates over the ``RECORD`` entries and returns a tuple
660
+ ``(path, hash, size)`` for each line.
661
+
662
+ :returns: iterator of (path, hash, size)
663
+ """
664
+ for result in self._get_records():
665
+ yield result
666
+
667
+ def write_installed_files(self, paths, prefix, dry_run=False):
668
+ """
669
+ Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any
670
+ existing ``RECORD`` file is silently overwritten.
671
+
672
+ prefix is used to determine when to write absolute paths.
673
+ """
674
+ prefix = os.path.join(prefix, '')
675
+ base = os.path.dirname(self.path)
676
+ base_under_prefix = base.startswith(prefix)
677
+ base = os.path.join(base, '')
678
+ record_path = self.get_distinfo_file('RECORD')
679
+ logger.info('creating %s', record_path)
680
+ if dry_run:
681
+ return None
682
+ with CSVWriter(record_path) as writer:
683
+ for path in paths:
684
+ if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')):
685
+ # do not put size and hash, as in PEP-376
686
+ hash_value = size = ''
687
+ else:
688
+ size = '%d' % os.path.getsize(path)
689
+ with open(path, 'rb') as fp:
690
+ hash_value = self.get_hash(fp.read())
691
+ if path.startswith(base) or (base_under_prefix and
692
+ path.startswith(prefix)):
693
+ path = os.path.relpath(path, base)
694
+ writer.writerow((path, hash_value, size))
695
+
696
+ # add the RECORD file itself
697
+ if record_path.startswith(base):
698
+ record_path = os.path.relpath(record_path, base)
699
+ writer.writerow((record_path, '', ''))
700
+ return record_path
701
+
702
+ def check_installed_files(self):
703
+ """
704
+ Checks that the hashes and sizes of the files in ``RECORD`` are
705
+ matched by the files themselves. Returns a (possibly empty) list of
706
+ mismatches. Each entry in the mismatch list will be a tuple consisting
707
+ of the path, 'exists', 'size' or 'hash' according to what didn't match
708
+ (existence is checked first, then size, then hash), the expected
709
+ value and the actual value.
710
+ """
711
+ mismatches = []
712
+ base = os.path.dirname(self.path)
713
+ record_path = self.get_distinfo_file('RECORD')
714
+ for path, hash_value, size in self.list_installed_files():
715
+ if not os.path.isabs(path):
716
+ path = os.path.join(base, path)
717
+ if path == record_path:
718
+ continue
719
+ if not os.path.exists(path):
720
+ mismatches.append((path, 'exists', True, False))
721
+ elif os.path.isfile(path):
722
+ actual_size = str(os.path.getsize(path))
723
+ if size and actual_size != size:
724
+ mismatches.append((path, 'size', size, actual_size))
725
+ elif hash_value:
726
+ if '=' in hash_value:
727
+ hasher = hash_value.split('=', 1)[0]
728
+ else:
729
+ hasher = None
730
+
731
+ with open(path, 'rb') as f:
732
+ actual_hash = self.get_hash(f.read(), hasher)
733
+ if actual_hash != hash_value:
734
+ mismatches.append((path, 'hash', hash_value, actual_hash))
735
+ return mismatches
736
+
737
+ @cached_property
738
+ def shared_locations(self):
739
+ """
740
+ A dictionary of shared locations whose keys are in the set 'prefix',
741
+ 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'.
742
+ The corresponding value is the absolute path of that category for
743
+ this distribution, and takes into account any paths selected by the
744
+ user at installation time (e.g. via command-line arguments). In the
745
+ case of the 'namespace' key, this would be a list of absolute paths
746
+ for the roots of namespace packages in this distribution.
747
+
748
+ The first time this property is accessed, the relevant information is
749
+ read from the SHARED file in the .dist-info directory.
750
+ """
751
+ result = {}
752
+ shared_path = os.path.join(self.path, 'SHARED')
753
+ if os.path.isfile(shared_path):
754
+ with codecs.open(shared_path, 'r', encoding='utf-8') as f:
755
+ lines = f.read().splitlines()
756
+ for line in lines:
757
+ key, value = line.split('=', 1)
758
+ if key == 'namespace':
759
+ result.setdefault(key, []).append(value)
760
+ else:
761
+ result[key] = value
762
+ return result
763
+
764
+ def write_shared_locations(self, paths, dry_run=False):
765
+ """
766
+ Write shared location information to the SHARED file in .dist-info.
767
+ :param paths: A dictionary as described in the documentation for
768
+ :meth:`shared_locations`.
769
+ :param dry_run: If True, the action is logged but no file is actually
770
+ written.
771
+ :return: The path of the file written to.
772
+ """
773
+ shared_path = os.path.join(self.path, 'SHARED')
774
+ logger.info('creating %s', shared_path)
775
+ if dry_run:
776
+ return None
777
+ lines = []
778
+ for key in ('prefix', 'lib', 'headers', 'scripts', 'data'):
779
+ path = paths[key]
780
+ if os.path.isdir(paths[key]):
781
+ lines.append('%s=%s' % (key, path))
782
+ for ns in paths.get('namespace', ()):
783
+ lines.append('namespace=%s' % ns)
784
+
785
+ with codecs.open(shared_path, 'w', encoding='utf-8') as f:
786
+ f.write('\n'.join(lines))
787
+ return shared_path
788
+
789
+ def get_distinfo_resource(self, path):
790
+ if path not in DIST_FILES:
791
+ raise DistlibException('invalid path for a dist-info file: '
792
+ '%r at %r' % (path, self.path))
793
+ finder = resources.finder_for_path(self.path)
794
+ if finder is None:
795
+ raise DistlibException('Unable to get a finder for %s' % self.path)
796
+ return finder.find(path)
797
+
798
+ def get_distinfo_file(self, path):
799
+ """
800
+ Returns a path located under the ``.dist-info`` directory. Returns a
801
+ string representing the path.
802
+
803
+ :parameter path: a ``'/'``-separated path relative to the
804
+ ``.dist-info`` directory or an absolute path;
805
+ If *path* is an absolute path and doesn't start
806
+ with the ``.dist-info`` directory path,
807
+ a :class:`DistlibException` is raised
808
+ :type path: str
809
+ :rtype: str
810
+ """
811
+ # Check if it is an absolute path # XXX use relpath, add tests
812
+ if path.find(os.sep) >= 0:
813
+ # it's an absolute path?
814
+ distinfo_dirname, path = path.split(os.sep)[-2:]
815
+ if distinfo_dirname != self.path.split(os.sep)[-1]:
816
+ raise DistlibException(
817
+ 'dist-info file %r does not belong to the %r %s '
818
+ 'distribution' % (path, self.name, self.version))
819
+
820
+ # The file must be relative
821
+ if path not in DIST_FILES:
822
+ raise DistlibException('invalid path for a dist-info file: '
823
+ '%r at %r' % (path, self.path))
824
+
825
+ return os.path.join(self.path, path)
826
+
827
+ def list_distinfo_files(self):
828
+ """
829
+ Iterates over the ``RECORD`` entries and returns paths for each line if
830
+ the path is pointing to a file located in the ``.dist-info`` directory
831
+ or one of its subdirectories.
832
+
833
+ :returns: iterator of paths
834
+ """
835
+ base = os.path.dirname(self.path)
836
+ for path, checksum, size in self._get_records():
837
+ # XXX add separator or use real relpath algo
838
+ if not os.path.isabs(path):
839
+ path = os.path.join(base, path)
840
+ if path.startswith(self.path):
841
+ yield path
842
+
843
+ def __eq__(self, other):
844
+ return (isinstance(other, InstalledDistribution) and
845
+ self.path == other.path)
846
+
847
+ # See http://docs.python.org/reference/datamodel#object.__hash__
848
+ __hash__ = object.__hash__
849
+
850
+
851
+ class EggInfoDistribution(BaseInstalledDistribution):
852
+ """Created with the *path* of the ``.egg-info`` directory or file provided
853
+ to the constructor. It reads the metadata contained in the file itself, or
854
+ if the given path happens to be a directory, the metadata is read from the
855
+ file ``PKG-INFO`` under that directory."""
856
+
857
+ requested = True # as we have no way of knowing, assume it was
858
+ shared_locations = {}
859
+
860
+ def __init__(self, path, env=None):
861
+ def set_name_and_version(s, n, v):
862
+ s.name = n
863
+ s.key = n.lower() # for case-insensitive comparisons
864
+ s.version = v
865
+
866
+ self.path = path
867
+ self.dist_path = env
868
+ if env and env._cache_enabled and path in env._cache_egg.path:
869
+ metadata = env._cache_egg.path[path].metadata
870
+ set_name_and_version(self, metadata.name, metadata.version)
871
+ else:
872
+ metadata = self._get_metadata(path)
873
+
874
+ # Need to be set before caching
875
+ set_name_and_version(self, metadata.name, metadata.version)
876
+
877
+ if env and env._cache_enabled:
878
+ env._cache_egg.add(self)
879
+ super(EggInfoDistribution, self).__init__(metadata, path, env)
880
+
881
+ def _get_metadata(self, path):
882
+ requires = None
883
+
884
+ def parse_requires_data(data):
885
+ """Create a list of dependencies from a requires.txt file.
886
+
887
+ *data*: the contents of a setuptools-produced requires.txt file.
888
+ """
889
+ reqs = []
890
+ lines = data.splitlines()
891
+ for line in lines:
892
+ line = line.strip()
893
+ if line.startswith('['):
894
+ logger.warning('Unexpected line: quitting requirement scan: %r',
895
+ line)
896
+ break
897
+ r = parse_requirement(line)
898
+ if not r:
899
+ logger.warning('Not recognised as a requirement: %r', line)
900
+ continue
901
+ if r.extras:
902
+ logger.warning('extra requirements in requires.txt are '
903
+ 'not supported')
904
+ if not r.constraints:
905
+ reqs.append(r.name)
906
+ else:
907
+ cons = ', '.join('%s%s' % c for c in r.constraints)
908
+ reqs.append('%s (%s)' % (r.name, cons))
909
+ return reqs
910
+
911
+ def parse_requires_path(req_path):
912
+ """Create a list of dependencies from a requires.txt file.
913
+
914
+ *req_path*: the path to a setuptools-produced requires.txt file.
915
+ """
916
+
917
+ reqs = []
918
+ try:
919
+ with codecs.open(req_path, 'r', 'utf-8') as fp:
920
+ reqs = parse_requires_data(fp.read())
921
+ except IOError:
922
+ pass
923
+ return reqs
924
+
925
+ if path.endswith('.egg'):
926
+ if os.path.isdir(path):
927
+ meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
928
+ metadata = Metadata(path=meta_path, scheme='legacy')
929
+ req_path = os.path.join(path, 'EGG-INFO', 'requires.txt')
930
+ requires = parse_requires_path(req_path)
931
+ else:
932
+ # FIXME handle the case where zipfile is not available
933
+ zipf = zipimport.zipimporter(path)
934
+ fileobj = StringIO(
935
+ zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'))
936
+ metadata = Metadata(fileobj=fileobj, scheme='legacy')
937
+ try:
938
+ data = zipf.get_data('EGG-INFO/requires.txt')
939
+ requires = parse_requires_data(data.decode('utf-8'))
940
+ except IOError:
941
+ requires = None
942
+ elif path.endswith('.egg-info'):
943
+ if os.path.isdir(path):
944
+ req_path = os.path.join(path, 'requires.txt')
945
+ requires = parse_requires_path(req_path)
946
+ path = os.path.join(path, 'PKG-INFO')
947
+ metadata = Metadata(path=path, scheme='legacy')
948
+ else:
949
+ raise DistlibException('path must end with .egg-info or .egg, '
950
+ 'got %r' % path)
951
+
952
+ if requires:
953
+ metadata.add_requirements(requires)
954
+ return metadata
955
+
956
+ def __repr__(self):
957
+ return '<EggInfoDistribution %r %s at %r>' % (
958
+ self.name, self.version, self.path)
959
+
960
+ def __str__(self):
961
+ return "%s %s" % (self.name, self.version)
962
+
963
+ def check_installed_files(self):
964
+ """
965
+ Checks that the hashes and sizes of the files in ``RECORD`` are
966
+ matched by the files themselves. Returns a (possibly empty) list of
967
+ mismatches. Each entry in the mismatch list will be a tuple consisting
968
+ of the path, 'exists', 'size' or 'hash' according to what didn't match
969
+ (existence is checked first, then size, then hash), the expected
970
+ value and the actual value.
971
+ """
972
+ mismatches = []
973
+ record_path = os.path.join(self.path, 'installed-files.txt')
974
+ if os.path.exists(record_path):
975
+ for path, _, _ in self.list_installed_files():
976
+ if path == record_path:
977
+ continue
978
+ if not os.path.exists(path):
979
+ mismatches.append((path, 'exists', True, False))
980
+ return mismatches
981
+
982
+ def list_installed_files(self):
983
+ """
984
+ Iterates over the ``installed-files.txt`` entries and returns a tuple
985
+ ``(path, hash, size)`` for each line.
986
+
987
+ :returns: a list of (path, hash, size)
988
+ """
989
+
990
+ def _md5(path):
991
+ f = open(path, 'rb')
992
+ try:
993
+ content = f.read()
994
+ finally:
995
+ f.close()
996
+ return hashlib.md5(content).hexdigest()
997
+
998
+ def _size(path):
999
+ return os.stat(path).st_size
1000
+
1001
+ record_path = os.path.join(self.path, 'installed-files.txt')
1002
+ result = []
1003
+ if os.path.exists(record_path):
1004
+ with codecs.open(record_path, 'r', encoding='utf-8') as f:
1005
+ for line in f:
1006
+ line = line.strip()
1007
+ p = os.path.normpath(os.path.join(self.path, line))
1008
+ # "./" is present as a marker between installed files
1009
+ # and installation metadata files
1010
+ if not os.path.exists(p):
1011
+ logger.warning('Non-existent file: %s', p)
1012
+ if p.endswith(('.pyc', '.pyo')):
1013
+ continue
1014
+ #otherwise fall through and fail
1015
+ if not os.path.isdir(p):
1016
+ result.append((p, _md5(p), _size(p)))
1017
+ result.append((record_path, None, None))
1018
+ return result
1019
+
1020
+ def list_distinfo_files(self, absolute=False):
1021
+ """
1022
+ Iterates over the ``installed-files.txt`` entries and returns paths for
1023
+ each line if the path is pointing to a file located in the
1024
+ ``.egg-info`` directory or one of its subdirectories.
1025
+
1026
+ :parameter absolute: If *absolute* is ``True``, each returned path is
1027
+ transformed into a local absolute path. Otherwise the
1028
+ raw value from ``installed-files.txt`` is returned.
1029
+ :type absolute: boolean
1030
+ :returns: iterator of paths
1031
+ """
1032
+ record_path = os.path.join(self.path, 'installed-files.txt')
1033
+ if os.path.exists(record_path):
1034
+ skip = True
1035
+ with codecs.open(record_path, 'r', encoding='utf-8') as f:
1036
+ for line in f:
1037
+ line = line.strip()
1038
+ if line == './':
1039
+ skip = False
1040
+ continue
1041
+ if not skip:
1042
+ p = os.path.normpath(os.path.join(self.path, line))
1043
+ if p.startswith(self.path):
1044
+ if absolute:
1045
+ yield p
1046
+ else:
1047
+ yield line
1048
+
1049
+ def __eq__(self, other):
1050
+ return (isinstance(other, EggInfoDistribution) and
1051
+ self.path == other.path)
1052
+
1053
+ # See http://docs.python.org/reference/datamodel#object.__hash__
1054
+ __hash__ = object.__hash__
1055
+
1056
+ new_dist_class = InstalledDistribution
1057
+ old_dist_class = EggInfoDistribution
1058
+
1059
+
1060
+ class DependencyGraph(object):
1061
+ """
1062
+ Represents a dependency graph between distributions.
1063
+
1064
+ The dependency relationships are stored in an ``adjacency_list`` that maps
1065
+ distributions to a list of ``(other, label)`` tuples where ``other``
1066
+ is a distribution and the edge is labeled with ``label`` (i.e. the version
1067
+ specifier, if such was provided). Also, for more efficient traversal, for
1068
+ every distribution ``x``, a list of predecessors is kept in
1069
+ ``reverse_list[x]``. An edge from distribution ``a`` to
1070
+ distribution ``b`` means that ``a`` depends on ``b``. If any missing
1071
+ dependencies are found, they are stored in ``missing``, which is a
1072
+ dictionary that maps distributions to a list of requirements that were not
1073
+ provided by any other distributions.
1074
+ """
1075
+
1076
+ def __init__(self):
1077
+ self.adjacency_list = {}
1078
+ self.reverse_list = {}
1079
+ self.missing = {}
1080
+
1081
+ def add_distribution(self, distribution):
1082
+ """Add the *distribution* to the graph.
1083
+
1084
+ :type distribution: :class:`distutils2.database.InstalledDistribution`
1085
+ or :class:`distutils2.database.EggInfoDistribution`
1086
+ """
1087
+ self.adjacency_list[distribution] = []
1088
+ self.reverse_list[distribution] = []
1089
+ #self.missing[distribution] = []
1090
+
1091
+ def add_edge(self, x, y, label=None):
1092
+ """Add an edge from distribution *x* to distribution *y* with the given
1093
+ *label*.
1094
+
1095
+ :type x: :class:`distutils2.database.InstalledDistribution` or
1096
+ :class:`distutils2.database.EggInfoDistribution`
1097
+ :type y: :class:`distutils2.database.InstalledDistribution` or
1098
+ :class:`distutils2.database.EggInfoDistribution`
1099
+ :type label: ``str`` or ``None``
1100
+ """
1101
+ self.adjacency_list[x].append((y, label))
1102
+ # multiple edges are allowed, so be careful
1103
+ if x not in self.reverse_list[y]:
1104
+ self.reverse_list[y].append(x)
1105
+
1106
+ def add_missing(self, distribution, requirement):
1107
+ """
1108
+ Add a missing *requirement* for the given *distribution*.
1109
+
1110
+ :type distribution: :class:`distutils2.database.InstalledDistribution`
1111
+ or :class:`distutils2.database.EggInfoDistribution`
1112
+ :type requirement: ``str``
1113
+ """
1114
+ logger.debug('%s missing %r', distribution, requirement)
1115
+ self.missing.setdefault(distribution, []).append(requirement)
1116
+
1117
+ def _repr_dist(self, dist):
1118
+ return '%s %s' % (dist.name, dist.version)
1119
+
1120
+ def repr_node(self, dist, level=1):
1121
+ """Prints only a subgraph"""
1122
+ output = [self._repr_dist(dist)]
1123
+ for other, label in self.adjacency_list[dist]:
1124
+ dist = self._repr_dist(other)
1125
+ if label is not None:
1126
+ dist = '%s [%s]' % (dist, label)
1127
+ output.append(' ' * level + str(dist))
1128
+ suboutput = self.repr_node(other, level + 1)
1129
+ subs = suboutput.split('\n')
1130
+ output.extend(subs[1:])
1131
+ return '\n'.join(output)
1132
+
1133
+ def to_dot(self, f, skip_disconnected=True):
1134
+ """Writes a DOT output for the graph to the provided file *f*.
1135
+
1136
+ If *skip_disconnected* is set to ``True``, then all distributions
1137
+ that are not dependent on any other distribution are skipped.
1138
+
1139
+ :type f: has to support ``file``-like operations
1140
+ :type skip_disconnected: ``bool``
1141
+ """
1142
+ disconnected = []
1143
+
1144
+ f.write("digraph dependencies {\n")
1145
+ for dist, adjs in self.adjacency_list.items():
1146
+ if len(adjs) == 0 and not skip_disconnected:
1147
+ disconnected.append(dist)
1148
+ for other, label in adjs:
1149
+ if not label is None:
1150
+ f.write('"%s" -> "%s" [label="%s"]\n' %
1151
+ (dist.name, other.name, label))
1152
+ else:
1153
+ f.write('"%s" -> "%s"\n' % (dist.name, other.name))
1154
+ if not skip_disconnected and len(disconnected) > 0:
1155
+ f.write('subgraph disconnected {\n')
1156
+ f.write('label = "Disconnected"\n')
1157
+ f.write('bgcolor = red\n')
1158
+
1159
+ for dist in disconnected:
1160
+ f.write('"%s"' % dist.name)
1161
+ f.write('\n')
1162
+ f.write('}\n')
1163
+ f.write('}\n')
1164
+
1165
+ def topological_sort(self):
1166
+ """
1167
+ Perform a topological sort of the graph.
1168
+ :return: A tuple, the first element of which is a topologically sorted
1169
+ list of distributions, and the second element of which is a
1170
+ list of distributions that cannot be sorted because they have
1171
+ circular dependencies and so form a cycle.
1172
+ """
1173
+ result = []
1174
+ # Make a shallow copy of the adjacency list
1175
+ alist = {}
1176
+ for k, v in self.adjacency_list.items():
1177
+ alist[k] = v[:]
1178
+ while True:
1179
+ # See what we can remove in this run
1180
+ to_remove = []
1181
+ for k, v in list(alist.items())[:]:
1182
+ if not v:
1183
+ to_remove.append(k)
1184
+ del alist[k]
1185
+ if not to_remove:
1186
+ # What's left in alist (if anything) is a cycle.
1187
+ break
1188
+ # Remove from the adjacency list of others
1189
+ for k, v in alist.items():
1190
+ alist[k] = [(d, r) for d, r in v if d not in to_remove]
1191
+ logger.debug('Moving to result: %s',
1192
+ ['%s (%s)' % (d.name, d.version) for d in to_remove])
1193
+ result.extend(to_remove)
1194
+ return result, list(alist.keys())
1195
+
1196
+ def __repr__(self):
1197
+ """Representation of the graph"""
1198
+ output = []
1199
+ for dist, adjs in self.adjacency_list.items():
1200
+ output.append(self.repr_node(dist))
1201
+ return '\n'.join(output)
1202
+
1203
+
1204
+ def make_graph(dists, scheme='default'):
1205
+ """Makes a dependency graph from the given distributions.
1206
+
1207
+ :parameter dists: a list of distributions
1208
+ :type dists: list of :class:`distutils2.database.InstalledDistribution` and
1209
+ :class:`distutils2.database.EggInfoDistribution` instances
1210
+ :rtype: a :class:`DependencyGraph` instance
1211
+ """
1212
+ scheme = get_scheme(scheme)
1213
+ graph = DependencyGraph()
1214
+ provided = {} # maps names to lists of (version, dist) tuples
1215
+
1216
+ # first, build the graph and find out what's provided
1217
+ for dist in dists:
1218
+ graph.add_distribution(dist)
1219
+
1220
+ for p in dist.provides:
1221
+ name, version = parse_name_and_version(p)
1222
+ logger.debug('Add to provided: %s, %s, %s', name, version, dist)
1223
+ provided.setdefault(name, []).append((version, dist))
1224
+
1225
+ # now make the edges
1226
+ for dist in dists:
1227
+ requires = (dist.run_requires | dist.meta_requires |
1228
+ dist.build_requires | dist.dev_requires)
1229
+ for req in requires:
1230
+ try:
1231
+ matcher = scheme.matcher(req)
1232
+ except UnsupportedVersionError:
1233
+ # XXX compat-mode if cannot read the version
1234
+ logger.warning('could not read version %r - using name only',
1235
+ req)
1236
+ name = req.split()[0]
1237
+ matcher = scheme.matcher(name)
1238
+
1239
+ name = matcher.key # case-insensitive
1240
+
1241
+ matched = False
1242
+ if name in provided:
1243
+ for version, provider in provided[name]:
1244
+ try:
1245
+ match = matcher.match(version)
1246
+ except UnsupportedVersionError:
1247
+ match = False
1248
+
1249
+ if match:
1250
+ graph.add_edge(dist, provider, req)
1251
+ matched = True
1252
+ break
1253
+ if not matched:
1254
+ graph.add_missing(dist, req)
1255
+ return graph
1256
+
1257
+
1258
+ def get_dependent_dists(dists, dist):
1259
+ """Recursively generate a list of distributions from *dists* that are
1260
+ dependent on *dist*.
1261
+
1262
+ :param dists: a list of distributions
1263
+ :param dist: a distribution, member of *dists* for which we are interested
1264
+ """
1265
+ if dist not in dists:
1266
+ raise DistlibException('given distribution %r is not a member '
1267
+ 'of the list' % dist.name)
1268
+ graph = make_graph(dists)
1269
+
1270
+ dep = [dist] # dependent distributions
1271
+ todo = graph.reverse_list[dist] # list of nodes we should inspect
1272
+
1273
+ while todo:
1274
+ d = todo.pop()
1275
+ dep.append(d)
1276
+ for succ in graph.reverse_list[d]:
1277
+ if succ not in dep:
1278
+ todo.append(succ)
1279
+
1280
+ dep.pop(0) # remove dist from dep, was there to prevent infinite loops
1281
+ return dep
1282
+
1283
+
1284
+ def get_required_dists(dists, dist):
1285
+ """Recursively generate a list of distributions from *dists* that are
1286
+ required by *dist*.
1287
+
1288
+ :param dists: a list of distributions
1289
+ :param dist: a distribution, member of *dists* for which we are interested
1290
+ """
1291
+ if dist not in dists:
1292
+ raise DistlibException('given distribution %r is not a member '
1293
+ 'of the list' % dist.name)
1294
+ graph = make_graph(dists)
1295
+
1296
+ req = [] # required distributions
1297
+ todo = graph.adjacency_list[dist] # list of nodes we should inspect
1298
+
1299
+ while todo:
1300
+ d = todo.pop()[0]
1301
+ req.append(d)
1302
+ for pred in graph.adjacency_list[d]:
1303
+ if pred not in req:
1304
+ todo.append(pred)
1305
+
1306
+ return req
1307
+
1308
+
1309
+ def make_dist(name, version, **kwargs):
1310
+ """
1311
+ A convenience method for making a dist given just a name and version.
1312
+ """
1313
+ summary = kwargs.pop('summary', 'Placeholder for summary')
1314
+ md = Metadata(**kwargs)
1315
+ md.name = name
1316
+ md.version = version
1317
+ md.summary = summary or 'Placeholder for summary'
1318
+ return Distribution(md)