diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/INSTALLER b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/INSTALLER rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/INSTALLER diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/LICENSE.txt b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/LICENSE.txt similarity index 100% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/LICENSE.txt rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/LICENSE.txt diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/METADATA b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/METADATA similarity index 94% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/METADATA rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/METADATA index 1413a04..959afb6 100644 --- a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/METADATA +++ b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/METADATA @@ -1,13 +1,14 @@ Metadata-Version: 2.1 Name: pip -Version: 20.1.1 +Version: 20.2.2 Summary: The PyPA recommended tool for installing Python packages. Home-page: https://pip.pypa.io/ Author: The pip developers -Author-email: pypa-dev@groups.google.com +Author-email: distutils-sig@python.org License: MIT Project-URL: Documentation, https://pip.pypa.io Project-URL: Source, https://github.com/pypa/pip +Project-URL: Changelog, https://pip.pypa.io/en/stable/news/ Keywords: distutils easy_install egg setuptools wheel virtualenv Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -79,7 +80,7 @@ rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. .. _learn more and take our survey: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html .. _Issue tracking: https://github.com/pypa/pip/issues .. _Discourse channel: https://discuss.python.org/c/packaging -.. _Development mailing list: https://groups.google.com/forum/#!forum/pypa-dev +.. _Development mailing list: https://mail.python.org/mailman3/lists/distutils-sig.python.org/ .. _User IRC: https://webchat.freenode.net/?channels=%23pypa .. _Development IRC: https://webchat.freenode.net/?channels=%23pypa-dev .. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/RECORD b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/RECORD similarity index 73% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/RECORD rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/RECORD index bc7a801..225a28e 100644 --- a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/RECORD +++ b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/RECORD @@ -1,14 +1,14 @@ ../../../bin/pip,sha256=xEtZHt_pC8ID4KBqXAaqKhGUGUBGTtvkhuwqkLbNfHw,266 ../../../bin/pip3,sha256=xEtZHt_pC8ID4KBqXAaqKhGUGUBGTtvkhuwqkLbNfHw,266 ../../../bin/pip3.8,sha256=xEtZHt_pC8ID4KBqXAaqKhGUGUBGTtvkhuwqkLbNfHw,266 -pip-20.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip-20.1.1.dist-info/LICENSE.txt,sha256=W6Ifuwlk-TatfRU2LR7W1JMcyMj5_y1NkRkOEJvnRDE,1090 -pip-20.1.1.dist-info/METADATA,sha256=dwRFheMvgIBpyZllM4tVlf5TfjoXc1ZxlsJf0ze61_M,3634 -pip-20.1.1.dist-info/RECORD,, -pip-20.1.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -pip-20.1.1.dist-info/entry_points.txt,sha256=HtfDOwpUlr9s73jqLQ6wF9V0_0qvUXJwCBz7Vwx0Ue0,125 -pip-20.1.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip/__init__.py,sha256=9lnkMA2mCKfgnTkqep7tMbosgEJ4rENcyu2tcqDwUNw,455 +pip-20.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-20.2.2.dist-info/LICENSE.txt,sha256=W6Ifuwlk-TatfRU2LR7W1JMcyMj5_y1NkRkOEJvnRDE,1090 +pip-20.2.2.dist-info/METADATA,sha256=ZMrQjU30hfRVRViE4qoVrwZzDSRyGCT8OdkDkkTMBv8,3708 +pip-20.2.2.dist-info/RECORD,, +pip-20.2.2.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +pip-20.2.2.dist-info/entry_points.txt,sha256=HtfDOwpUlr9s73jqLQ6wF9V0_0qvUXJwCBz7Vwx0Ue0,125 +pip-20.2.2.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=fZ401OZGdv55YGsuCEhsvdNa4YCZLmK457SN3qrstJk,455 pip/__main__.py,sha256=bqCAM1cj1HwHCDx3WJa-LJxOBXimGxE8OjBqAvnhVg0,911 pip/__pycache__/__init__.cpython-38.pyc,, pip/__pycache__/__main__.cpython-38.pyc,, @@ -23,8 +23,8 @@ pip/_internal/__pycache__/main.cpython-38.pyc,, pip/_internal/__pycache__/pyproject.cpython-38.pyc,, pip/_internal/__pycache__/self_outdated_check.cpython-38.pyc,, pip/_internal/__pycache__/wheel_builder.cpython-38.pyc,, -pip/_internal/build_env.py,sha256=2P0xaKpDhEfrA5P7cXnbx9QpL52Hc1Uturp8EIcjGRg,7506 -pip/_internal/cache.py,sha256=aXPdcihRKQVH26jl1cxSKTmTnV0_hNMs7cGADMUFi1Y,12334 +pip/_internal/build_env.py,sha256=9_UaQ2fpsBvpKAji27f7bPAi2v3mb0cBvDYcejwFKNM,8088 +pip/_internal/cache.py,sha256=pT17VVxgzZK32aqY5FRS8GyAI73LKzNMF8ZelQ7Ojm0,12249 pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 pip/_internal/cli/__pycache__/__init__.cpython-38.pyc,, pip/_internal/cli/__pycache__/autocompletion.cpython-38.pyc,, @@ -39,14 +39,14 @@ pip/_internal/cli/__pycache__/req_command.cpython-38.pyc,, pip/_internal/cli/__pycache__/spinners.cpython-38.pyc,, pip/_internal/cli/__pycache__/status_codes.cpython-38.pyc,, pip/_internal/cli/autocompletion.py,sha256=ekGNtcDI0p7rFVc-7s4T9Tbss4Jgb7vsB649XJIblRg,6547 -pip/_internal/cli/base_command.py,sha256=O5fT5HHfc_UYNvhqK0rjfh_13K3fIVQzcKUF4xKbFts,8024 -pip/_internal/cli/cmdoptions.py,sha256=SjHNqaQ49FO0VKshjoazPVFb7iDx71FKW_N4KgcS3qQ,28403 +pip/_internal/cli/base_command.py,sha256=BWTztM4b6h8hodDHDKjgJ82jaSeru2AILAJxi1d_IP8,8810 +pip/_internal/cli/cmdoptions.py,sha256=M_BtuqeyRpZAUUYytts3pguBCF2RaGukVpDPE0niroI,28782 pip/_internal/cli/command_context.py,sha256=ygMVoTy2jpNilKT-6416gFSQpaBtrKRBbVbi2fy__EU,975 pip/_internal/cli/main.py,sha256=Hxc9dZyW3xiDsYZX-_J2cGXT5DWNLNn_Y7o9oUme-Ec,2616 pip/_internal/cli/main_parser.py,sha256=voAtjo4WVPIYeu7Fqabva9SXaB3BjG0gH93GBfe6jHQ,2843 pip/_internal/cli/parser.py,sha256=4FfwW8xB84CrkLs35ud90ZkhCcWyVkx17XD6j3XCW7c,9480 -pip/_internal/cli/progress_bars.py,sha256=WtKOHkePvHwnlhDUotAmKpjBH6hBdVTOnxSiiuCC2l8,9031 -pip/_internal/cli/req_command.py,sha256=NajtG3IfB3YkiM7LANLttyJTfPtgB-3CTErY0YR0k50,15309 +pip/_internal/cli/progress_bars.py,sha256=J1zykt2LI4gbBeXorfYRmYV5FgXhcW4x3r6xE_a7Z7c,9121 +pip/_internal/cli/req_command.py,sha256=Eiz8TVzeqzG-40t7qLC1vO-vzjCRvX9C-qXMyfw9D1I,15132 pip/_internal/cli/spinners.py,sha256=PS9s53LB5aDPelIn8FhKerK3bOdgeefFH5wSWJ2PCzI,5509 pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 pip/_internal/commands/__init__.py,sha256=yoLAnmEXjoQgYfDuwsuWG3RzzD19oeHobGEhmpIYsB4,4100 @@ -66,22 +66,22 @@ pip/_internal/commands/__pycache__/search.cpython-38.pyc,, pip/_internal/commands/__pycache__/show.cpython-38.pyc,, pip/_internal/commands/__pycache__/uninstall.cpython-38.pyc,, pip/_internal/commands/__pycache__/wheel.cpython-38.pyc,, -pip/_internal/commands/cache.py,sha256=LZCLVEYCr5Ugh81Zt07Hz5v6SIt0QQzr2-npj3M44aE,5676 +pip/_internal/commands/cache.py,sha256=U3rLjls0AMMO8PxnhXVwIp7Biyvns8-gBThKTH3tX7Y,5676 pip/_internal/commands/check.py,sha256=fqRrz2uKPC8Qsx2rgLygAD2Rbr-qxp1Q55zUoyZzB9Q,1677 -pip/_internal/commands/completion.py,sha256=BoEW3RZQZhsZe5to1aOe245dcBLkf-PTCJL7_u9A-Es,2957 -pip/_internal/commands/configuration.py,sha256=y74Vl2p41dBOE2NwUzW4YqnbGbl9r0lsCyBlHguDAWA,7206 -pip/_internal/commands/debug.py,sha256=3YkY_M-h1tfpEzat4agulzAk17MU93Qt7ehy_Gi2l6Q,7275 -pip/_internal/commands/download.py,sha256=thDfHi0qY6DQ_1GkYPTutwta3tA0RaHJhKycepC4FgA,4740 -pip/_internal/commands/freeze.py,sha256=LveCd11SlrZ7s3RovnWpaK0tXB2Jci4DPcP6A9cj0e4,3342 -pip/_internal/commands/hash.py,sha256=KckEd5FeomfsRgZmRzhJRPYSsz-HXbFZGNdrzp12ftQ,1742 -pip/_internal/commands/help.py,sha256=s8bDMJbRVxs9ehLKuD4mXTsv1bTRapy1jDwaTCE90qw,1193 -pip/_internal/commands/install.py,sha256=1oXXadnvHM-55XWIdeqdaR75BjLPGC2-wXEkkhMyMtQ,25430 -pip/_internal/commands/list.py,sha256=GCrhhu07cw3CyZGFk8rGM2ejk3JmeF9XLbC7raiq_bw,10141 -pip/_internal/commands/search.py,sha256=KjAz-s9mwkiLfDd-cpQO3pL6KFoOyl1RKlvxnJj3zz8,5191 -pip/_internal/commands/show.py,sha256=RqSX_KvzcZWz1gxIOZEnnk4-VeSkNvr0yWz5jF6JrcY,6791 -pip/_internal/commands/uninstall.py,sha256=D2Otze7J-RJvjfozRq2Yon9NKJrg4cbBGFuXyEwBMR0,3202 -pip/_internal/commands/wheel.py,sha256=oxyo51V1m_Hu4U-HCS53vBx5-82Q6GOhn1doOgAr3KE,6431 -pip/_internal/configuration.py,sha256=k3Y3HMMMm_fzNqX75QoNuHvjX8tplmNBuIJJpDHmf9M,14349 +pip/_internal/commands/completion.py,sha256=ObssM77quf61qvbuSE6XLwUBdm_WcWIvXFI-Hy1RBsI,3081 +pip/_internal/commands/configuration.py,sha256=IN2QBF653sRiRU7-pHTpnZ6_gyiXNKUQkLiLaNRLKNw,9344 +pip/_internal/commands/debug.py,sha256=otBZnpnostX2kmYyOl6g6CeCLmk6H00Tsj2CDsCtFXw,7314 +pip/_internal/commands/download.py,sha256=EKFlj_ceGUEJj6yCDw7P6w7yUoB16IcNHhT2qnCFDNQ,4918 +pip/_internal/commands/freeze.py,sha256=vLBBP1d8wgEXrmlh06hbz_x_Q1mWHUdiWDa9NP2eKLE,3452 +pip/_internal/commands/hash.py,sha256=v2nYCiEsEI9nEam1p6GwdG8xyj5gFv-4WrqvNexKmeY,1843 +pip/_internal/commands/help.py,sha256=ryuMDt2tc7ic3NJYMjjoNRH5r6LrB2yQVZvehAm8bLs,1270 +pip/_internal/commands/install.py,sha256=h2L8vS6t2DbGAdttkdZmMucK2eJG2CYvcwhDa7AdKrQ,28683 +pip/_internal/commands/list.py,sha256=2o3rYw37ECrhe4-Bu5s_2C0bwhYgghh4833MxcWAEug,11312 +pip/_internal/commands/search.py,sha256=1HPAFU-tmgKrHhr4xNuk3xMoPeSzD_oDvDDiUFZZ15E,5756 +pip/_internal/commands/show.py,sha256=r69-G8HIepDKm4SeyeHj0Ez1P9xoihrpVUyXm6NmXYY,6996 +pip/_internal/commands/uninstall.py,sha256=Ys8hwFsg0kvvGtLGYG3ibL5BKvURhlSlCX50ZQ-hsHk,3311 +pip/_internal/commands/wheel.py,sha256=-HSISE5AV29I752Aqw4DdmulrGd8rB_ZTOdpbJ6T8iM,6419 +pip/_internal/configuration.py,sha256=-Gxz2J-KuvxiqWIJ9F-XnYVZ5lKhNk7VO6ondEbH4EM,14115 pip/_internal/distributions/__init__.py,sha256=ECBUW5Gtu9TjJwyFLvim-i6kUMYVuikNh9I5asL6tbA,959 pip/_internal/distributions/__pycache__/__init__.cpython-38.pyc,, pip/_internal/distributions/__pycache__/base.cpython-38.pyc,, @@ -92,14 +92,14 @@ pip/_internal/distributions/base.py,sha256=ruprpM_L2T2HNi3KLUHlbHimZ1sWVw-3Q0Lb8 pip/_internal/distributions/installed.py,sha256=YqlkBKr6TVP1MAYS6SG8ojud21wVOYLMZ8jMLJe9MSU,760 pip/_internal/distributions/sdist.py,sha256=D4XTMlCwgPlK69l62GLYkNSVTVe99fR5iAcVt2EbGok,4086 pip/_internal/distributions/wheel.py,sha256=95uD-TfaYoq3KiKBdzk9YMN4RRqJ28LNoSTS2K46gek,1294 -pip/_internal/exceptions.py,sha256=B3tSkzheqSfGoGt5OcAOhLhfnWWMzfJ60URvZWwkwHw,10308 +pip/_internal/exceptions.py,sha256=ZVpArxQrSlm4qAMtHaY3nHvG_t5eSi3WCnMowdm_m8I,12637 pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30 pip/_internal/index/__pycache__/__init__.cpython-38.pyc,, pip/_internal/index/__pycache__/collector.cpython-38.pyc,, pip/_internal/index/__pycache__/package_finder.cpython-38.pyc,, -pip/_internal/index/collector.py,sha256=tFpQdkBlbNzdwlep7a5_o9unymgWuEmo2WtARsagiao,21547 -pip/_internal/index/package_finder.py,sha256=2Uq4RPSRboyRPj1Zp3-SB8ZFNLAEMrZv6G2yH-wVjIA,37676 -pip/_internal/locations.py,sha256=VifFEqhc7FWFV8QGoEM3CpECRY8Doq7kTytytxsEgx0,6734 +pip/_internal/index/collector.py,sha256=rMdGdAABOrvIl0DYlCMWXr7mIoqrU2VGeQpCuWiPu1Q,22838 +pip/_internal/index/package_finder.py,sha256=ISieDd20dOSndMNybafCu3pO2JR3BKOfHv92Bes0j0Q,37364 +pip/_internal/locations.py,sha256=7YjzJy2CroQD8GBMemnHWRl9448BSIt0lfH98B-Dkd8,6732 pip/_internal/main.py,sha256=IVBnUQ-FG7DK6617uEXRB5_QJqspAsBFmTmTesYkbdQ,437 pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 pip/_internal/models/__pycache__/__init__.cpython-38.pyc,, @@ -113,30 +113,32 @@ pip/_internal/models/__pycache__/search_scope.cpython-38.pyc,, pip/_internal/models/__pycache__/selection_prefs.cpython-38.pyc,, pip/_internal/models/__pycache__/target_python.cpython-38.pyc,, pip/_internal/models/__pycache__/wheel.cpython-38.pyc,, -pip/_internal/models/candidate.py,sha256=Y58Bcm6oXUj0iS-yhmerlGo5CQJI2p0Ww9h6hR9zQDw,1150 +pip/_internal/models/candidate.py,sha256=gACeCSHTIaWuB6RAeLmGJnbFFbKfp_47UERDoC_ldOU,1195 pip/_internal/models/direct_url.py,sha256=MnBLPci1hE9Ndh6d3m0LAqB7hX3ci80CCJTE5eerFaQ,6900 -pip/_internal/models/format_control.py,sha256=ICzVjjGwfZYdX-eLLKHjMHLutEJlAGpfj09OG_eMqac,2673 -pip/_internal/models/index.py,sha256=K59A8-hVhBM20Xkahr4dTwP7OjkJyEqXH11UwHFVgqM,1060 -pip/_internal/models/link.py,sha256=KobEaGViwOzyPBD7kgzpGqyrQfh5zjlonOStCGAhl2U,7302 -pip/_internal/models/scheme.py,sha256=vvhBrrno7eVDXcdKHiZWwxhPHf4VG5uSCEkC0QDR2RU,679 -pip/_internal/models/search_scope.py,sha256=AYbFyfEen5cx0kRZTMgUWUxzcMr5nDk32MO4S67Ror4,4712 -pip/_internal/models/selection_prefs.py,sha256=rPeif2KKjhTPXeMoQYffjqh10oWpXhdkxRDaPT1HO8k,1908 -pip/_internal/models/target_python.py,sha256=bbOSwPmojPMtCW6i2XMNjVJzt_2GQYfx3FcGQY8pL44,3842 +pip/_internal/models/format_control.py,sha256=RdnnmXxVJppCZWzWEmFTr-zD_m3G0izPLqJi6Iop75M,2823 +pip/_internal/models/index.py,sha256=carvxxaT7mJyoEkptaECHUZiNaA6R5NrsGF55zawNn8,1161 +pip/_internal/models/link.py,sha256=FMlxvqKmLoj7xTQSgKqfO2ehE1WcgD4C5DmEBuC_Qos,7470 +pip/_internal/models/scheme.py,sha256=EhPkT_6G0Md84JTLSVopYsp5H_K6BREYmFvU8H6wMK8,778 +pip/_internal/models/search_scope.py,sha256=Lum0mY4_pdR9DDBy6HV5xHGIMPp_kU8vMsqYKFHZip4,4751 +pip/_internal/models/selection_prefs.py,sha256=pgNjTfgePPiX1R5S2S8Yc6odOfU9NzG7YP_m_gnS0kw,2044 +pip/_internal/models/target_python.py,sha256=R7tAXI15B_cgw7Fgnq5cI9F-44goUZncH9JMtE8pXRw,4034 pip/_internal/models/wheel.py,sha256=FTfzVb4WIbfIehxhdlAVvCil_MQ0-W44oyN56cE6NHc,2772 pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50 pip/_internal/network/__pycache__/__init__.cpython-38.pyc,, pip/_internal/network/__pycache__/auth.cpython-38.pyc,, pip/_internal/network/__pycache__/cache.cpython-38.pyc,, pip/_internal/network/__pycache__/download.cpython-38.pyc,, +pip/_internal/network/__pycache__/lazy_wheel.cpython-38.pyc,, pip/_internal/network/__pycache__/session.cpython-38.pyc,, pip/_internal/network/__pycache__/utils.cpython-38.pyc,, pip/_internal/network/__pycache__/xmlrpc.cpython-38.pyc,, -pip/_internal/network/auth.py,sha256=HJg5peC3gL44H7pmZhCPnu2MrwpAalOSF7d1jmNDqt8,11125 -pip/_internal/network/cache.py,sha256=51CExcRkXWrgMZ7WsrZ6cmijKfViD5tVgKbBvJHO1IE,2394 -pip/_internal/network/download.py,sha256=MIisedL1oFOSrYAN119HDlIuFfw6eL6CNY7oJhHIzUc,6269 +pip/_internal/network/auth.py,sha256=dt3NvTRJ8182S3VpdYFEZMPT0JhOKHyFtR-O-JMlJII,11652 +pip/_internal/network/cache.py,sha256=6cCD7XNrqh1d1lOSY5U-0ZXOG1YwEgMYs-VhRZVyzMA,2329 +pip/_internal/network/download.py,sha256=VTGDO01_nX-5MCdatd4Icv0F88_M8N3WnW6BevA6a0o,5151 +pip/_internal/network/lazy_wheel.py,sha256=RXcQILT5v5UO6kxgv76CSncLTqRL29o-OXbaW2aK7t4,8138 pip/_internal/network/session.py,sha256=Zs0uiyPxTpfpgSv-ZI9hK9TjasmTplBuBivOTcUiJME,15208 -pip/_internal/network/utils.py,sha256=iiixo1OeaQ3niUWiBjg59PN6f1w7vvTww1vFriTD_IU,1959 -pip/_internal/network/xmlrpc.py,sha256=AL115M3vFJ8xiHVJneb8Hi0ZFeRvdPhblC89w25OG5s,1597 +pip/_internal/network/utils.py,sha256=ZPHg7u6DEcg2EvILIdPECnvPLp21OPHxNVmeXfMy-n0,4172 +pip/_internal/network/xmlrpc.py,sha256=PFCiX_nnwYxC8SFIf7J3trP40ECGjA6fl2-IVNhbkPM,1882 pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/operations/__pycache__/__init__.cpython-38.pyc,, pip/_internal/operations/__pycache__/check.cpython-38.pyc,, @@ -148,23 +150,23 @@ pip/_internal/operations/build/__pycache__/metadata.cpython-38.pyc,, pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-38.pyc,, pip/_internal/operations/build/__pycache__/wheel.cpython-38.pyc,, pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-38.pyc,, -pip/_internal/operations/build/metadata.py,sha256=yHMi5gHYXcXyHcvUPWHdO-UyOo3McFWljn_nHfM1O9c,1307 +pip/_internal/operations/build/metadata.py,sha256=2aILgWCQTF1aIhWuCH8TTSjv_kYmA3x1262fT2FQ6pQ,1254 pip/_internal/operations/build/metadata_legacy.py,sha256=VgzBTk8naIO8-8N_ifEYF7ZAxWUDhphWVIaVlZ2FqYM,2011 -pip/_internal/operations/build/wheel.py,sha256=ntltdNP6D2Tpr4V0agssu6rE0F9LaBpJkYT6zSdhEbw,1469 +pip/_internal/operations/build/wheel.py,sha256=33vdkxTO-gNqrtWH1eNL_uZo4Irax85moDx2o9zae3M,1465 pip/_internal/operations/build/wheel_legacy.py,sha256=N1aqNZyGURBX0Bj6wPmB0t4866oMbxoHUpC9pz6FyT0,3356 -pip/_internal/operations/check.py,sha256=a6uHG0daoWpmSPCdL7iYJaGQYZ-CRvPvTnCv2PnIIs0,5353 -pip/_internal/operations/freeze.py,sha256=mGT2OFjMOb0FlVjgedAzJ9GbNOgNwYiL0130xx60pHA,10587 +pip/_internal/operations/check.py,sha256=JYDsVLvpFyJuJq0ttStgg8TRKbc0myYFAMnfnnQOREM,5215 +pip/_internal/operations/freeze.py,sha256=_vJSZwHBNzBV0GpRUSXhUJz3BrGFdcT2aTcWxH1L4P0,10373 pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51 pip/_internal/operations/install/__pycache__/__init__.cpython-38.pyc,, pip/_internal/operations/install/__pycache__/editable_legacy.cpython-38.pyc,, pip/_internal/operations/install/__pycache__/legacy.cpython-38.pyc,, pip/_internal/operations/install/__pycache__/wheel.cpython-38.pyc,, pip/_internal/operations/install/editable_legacy.py,sha256=rJ_xs2qtDUjpY2-n6eYlVyZiNoKbOtZXZrYrcnIELt4,1488 -pip/_internal/operations/install/legacy.py,sha256=YkKdL_tyNwDP2huOGxmopySh5Pz2v_wRVeSTEa6ZUco,4686 -pip/_internal/operations/install/wheel.py,sha256=8IO3GYTtrJ42cqipLOh0rxex4j-PfU8m71HVB0tOQd0,23885 -pip/_internal/operations/prepare.py,sha256=RDwtSetVTfv-nv1-_apYBA3Dez5ngBmOYzcuZy2Q3vk,20030 +pip/_internal/operations/install/legacy.py,sha256=zu3Gw54dgHtluyW5n8j5qKcAScidQXJvqB8fb0oLB-4,4281 +pip/_internal/operations/install/wheel.py,sha256=nJmOSOYY3keksXd_3GFuhAWeeoKvGOyoSGbjXABjZ40,31310 +pip/_internal/operations/prepare.py,sha256=Rt7Yh7w10_Q-vI3b7R1wkt2R6XPX8YVUdODk-TaGI9c,19903 pip/_internal/pyproject.py,sha256=VJKsrXORGiGoDPVKCQhuu4tWlQSTOhoiRlVLRNu4rx4,7400 -pip/_internal/req/__init__.py,sha256=UVaYPlHZVGRBQQPjvGC_6jJDQtewXm0ws-8Lxhg_TiY,2671 +pip/_internal/req/__init__.py,sha256=s-E5Vxxqqpcs7xfY5gY69oHogsWJ4sLbnUiDoWmkHOU,3133 pip/_internal/req/__pycache__/__init__.cpython-38.pyc,, pip/_internal/req/__pycache__/constructors.cpython-38.pyc,, pip/_internal/req/__pycache__/req_file.cpython-38.pyc,, @@ -172,12 +174,12 @@ pip/_internal/req/__pycache__/req_install.cpython-38.pyc,, pip/_internal/req/__pycache__/req_set.cpython-38.pyc,, pip/_internal/req/__pycache__/req_tracker.cpython-38.pyc,, pip/_internal/req/__pycache__/req_uninstall.cpython-38.pyc,, -pip/_internal/req/constructors.py,sha256=i_dU2sYtSk5GMsad68gBx26tfneRmhPF2sYGe4uPnu8,15441 -pip/_internal/req/req_file.py,sha256=5QlZr36kkw1Jsbr8vFO-fGUEAef9h-AoRRqjx8EYSuQ,19075 -pip/_internal/req/req_install.py,sha256=9yn_fBFTyzHPMjYG5WXBB2WiGQvk3BT6gYl9Khw8ZoE,31713 -pip/_internal/req/req_set.py,sha256=EBHZ9zWSR8arxjcadyU2OotZIECemM8oOFQ0nK-Bb7E,7792 -pip/_internal/req/req_tracker.py,sha256=cAKhSw-QbhGxqPF1Wc0zD6jo932jpdYF3ROfRSH8hes,4744 -pip/_internal/req/req_uninstall.py,sha256=NdErRQBpNScsdwJAo3O_zo2KPPfQyVMJ_Q2mxPWYyOA,23734 +pip/_internal/req/constructors.py,sha256=LrSHbRHu52-h6HM1qJKG68o1Jw5q8MvJGfr4As6j2uU,16387 +pip/_internal/req/req_file.py,sha256=p7n3Y0q275Eisqfxd0vtfnxYvlT6TCCY0tj75p-yiOY,19448 +pip/_internal/req/req_install.py,sha256=hqQMm6s7f1a2RAGasyAhy47eZGom3OBKeiRM60xxdPg,33080 +pip/_internal/req/req_set.py,sha256=dxcfbieWYfYkTJNE07U8xaO40zLxl8BhWOcIHVFTmoo,7886 +pip/_internal/req/req_tracker.py,sha256=qWaiejNK6o6cqeyTOIGKIU1CoyrXCcqgMHYi3cqelOA,4690 +pip/_internal/req/req_uninstall.py,sha256=opMGDGb7ZaFippRbaarJaljtzl2CNZmBGEUSnTubE-A,23706 pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/resolution/__pycache__/__init__.cpython-38.pyc,, pip/_internal/resolution/__pycache__/base.cpython-38.pyc,, @@ -185,7 +187,7 @@ pip/_internal/resolution/base.py,sha256=xi72YmIS-lEjyK13PN_3qkGGthA4yGoK0C6qWyny pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/resolution/legacy/__pycache__/__init__.cpython-38.pyc,, pip/_internal/resolution/legacy/__pycache__/resolver.cpython-38.pyc,, -pip/_internal/resolution/legacy/resolver.py,sha256=56GuGHWcseV24cvTCOuRHMAF_Er1UeDxn5m18XMkHBs,17587 +pip/_internal/resolution/legacy/resolver.py,sha256=d-qW6UUxbZqKyXmX2bqnW5C8UtnO0ZcsQuKw_QXualc,18755 pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-38.pyc,, pip/_internal/resolution/resolvelib/__pycache__/base.cpython-38.pyc,, @@ -194,18 +196,19 @@ pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-38.pyc,, pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-38.pyc,, pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-38.pyc,, pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-38.pyc,, -pip/_internal/resolution/resolvelib/base.py,sha256=l2Z3-1Qg243lWzwFbaN17qixA4U8LYr-qMhZTdaHROc,1502 -pip/_internal/resolution/resolvelib/candidates.py,sha256=wzi9t3aX1Twi3xTNEkpG6eWOd0dEg5uKul-QkK3arvw,15173 -pip/_internal/resolution/resolvelib/factory.py,sha256=mDs3p8D9N9zfYvn_iIx0saDLHF1SF7KHBQlA1gWSWVQ,7574 -pip/_internal/resolution/resolvelib/provider.py,sha256=0fKuPuEoD5T7w-YwKgQZc1AmgSnAkrxGnLBOf-_6Kiw,1703 -pip/_internal/resolution/resolvelib/requirements.py,sha256=bu9Y4YINHjvBm-NBKvnxw9IYHW4t6rRlm4-QKVqLDsM,3872 -pip/_internal/resolution/resolvelib/resolver.py,sha256=3LXhhCz6CtIpih8tK2nHeRvVEjVJmXrqxNCM1FQM1U0,6673 -pip/_internal/self_outdated_check.py,sha256=3KO1pTJUuYaiV9X0t87I9PimkGL82HbhLWbocqKZpBU,8009 +pip/_internal/resolution/resolvelib/base.py,sha256=n8Rilea9jCzhlbtFiJKwCwIQSPW0ATjEKsCc0Vpm894,2342 +pip/_internal/resolution/resolvelib/candidates.py,sha256=RHo9r9g25FWzufKv93Ti9nS4hvAPUrhAjSDL7GCZFNQ,20339 +pip/_internal/resolution/resolvelib/factory.py,sha256=--ahYsr-r9zIhdyJJ1ZuETgaQrWiPIqwILWiMDn1IIU,17169 +pip/_internal/resolution/resolvelib/provider.py,sha256=BP8nh07Z1FlcT-Iaw4FblRM-DjUeUkiItKdKARYeM6M,6134 +pip/_internal/resolution/resolvelib/requirements.py,sha256=lGvoHRhkusRfaz4cFxYBoQNqxS6TeuO3K68qlui6g-0,4511 +pip/_internal/resolution/resolvelib/resolver.py,sha256=kI8g0NVlYIsDMRmDplWQdox6WO-0H7CI2wN-1ixnaew,10149 +pip/_internal/self_outdated_check.py,sha256=q6_nqUHPpt-DScwD97h7FCSqd4nI1s-xkpOI4I5Za3Y,6779 pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/utils/__pycache__/__init__.cpython-38.pyc,, pip/_internal/utils/__pycache__/appdirs.cpython-38.pyc,, pip/_internal/utils/__pycache__/compat.cpython-38.pyc,, pip/_internal/utils/__pycache__/compatibility_tags.cpython-38.pyc,, +pip/_internal/utils/__pycache__/datetime.cpython-38.pyc,, pip/_internal/utils/__pycache__/deprecation.cpython-38.pyc,, pip/_internal/utils/__pycache__/direct_url_helpers.cpython-38.pyc,, pip/_internal/utils/__pycache__/distutils_args.cpython-38.pyc,, @@ -220,6 +223,7 @@ pip/_internal/utils/__pycache__/logging.cpython-38.pyc,, pip/_internal/utils/__pycache__/misc.cpython-38.pyc,, pip/_internal/utils/__pycache__/models.cpython-38.pyc,, pip/_internal/utils/__pycache__/packaging.cpython-38.pyc,, +pip/_internal/utils/__pycache__/parallel.cpython-38.pyc,, pip/_internal/utils/__pycache__/pkg_resources.cpython-38.pyc,, pip/_internal/utils/__pycache__/setuptools_build.cpython-38.pyc,, pip/_internal/utils/__pycache__/subprocess.cpython-38.pyc,, @@ -230,31 +234,33 @@ pip/_internal/utils/__pycache__/urls.cpython-38.pyc,, pip/_internal/utils/__pycache__/virtualenv.cpython-38.pyc,, pip/_internal/utils/__pycache__/wheel.cpython-38.pyc,, pip/_internal/utils/appdirs.py,sha256=RZzUG-Bkh2b-miX0DSZ3v703_-bgK-v0PfWCCjwVE9g,1349 -pip/_internal/utils/compat.py,sha256=ZRJsXMjq373p0US54CUkKRkpLH-ioOM3H3yAhmbUPcs,8898 -pip/_internal/utils/compatibility_tags.py,sha256=b2NWEbxfsrB2pBLwJkNVSYUrIAsumQ2IWDoNabbwLPs,5492 +pip/_internal/utils/compat.py,sha256=GoCSUMoUmTGeg5irQGLDZ7v12As87yHrMzBXEke-njg,8865 +pip/_internal/utils/compatibility_tags.py,sha256=EtBJj-pstj_U0STUZ8FjlG7YDTjuRZUy6GY1cM86yv8,5439 +pip/_internal/utils/datetime.py,sha256=KL-vIdGU9JIpGB5NYkmwXWkH-G_2mvvABlmRtoSZsao,295 pip/_internal/utils/deprecation.py,sha256=pBnNogoA4UGTxa_JDnPXBRRYpKMbExAhXpBwAwklOBs,3318 pip/_internal/utils/direct_url_helpers.py,sha256=bZCBNwPQVyZpYGjX_VcomvVvRHvKw-9JzEV-Ft09LQc,4359 pip/_internal/utils/distutils_args.py,sha256=a56mblNxk9BGifbpEETG61mmBrqhjtjRkJ4HYn-oOEE,1350 -pip/_internal/utils/encoding.py,sha256=hxZz0t3Whw3d4MHQEiofxalTlfKwxFdLc8fpeGfhKo8,1320 +pip/_internal/utils/encoding.py,sha256=wHDJ25yCT_T4ySscCL3P978OpLrfDCpitg8D64IEXMY,1284 pip/_internal/utils/entrypoints.py,sha256=vHcNpnksCv6mllihU6hfifdsKPEjwcaJ1aLIXEaynaU,1152 -pip/_internal/utils/filesystem.py,sha256=fqpFwT280152rlX1RjJqjoLp_MXVA8HzKTtDmsl15Ps,6813 +pip/_internal/utils/filesystem.py,sha256=-fU3XteCAIJwf_9FvCZU7vhywvt3nuf_cqkCdwgy1Y8,6943 pip/_internal/utils/filetypes.py,sha256=R2FwzoeX7b-rZALOXx5cuO8VPPMhUQ4ne7wm3n3IcWA,571 pip/_internal/utils/glibc.py,sha256=LOeNGgawCKS-4ke9fii78fwXD73dtNav3uxz1Bf-Ab8,3297 -pip/_internal/utils/hashes.py,sha256=LQVOt2LTWAdBJH6WPim1YGdF0J-0AfBBLghIDlY1-80,3986 +pip/_internal/utils/hashes.py,sha256=xHmrqNwC1eBN0oY0R_RXLJLXGvFdo5gwmbz_pas94k8,4358 pip/_internal/utils/inject_securetransport.py,sha256=M17ZlFVY66ApgeASVjKKLKNz0LAfk-SyU0HZ4ZB6MmI,810 pip/_internal/utils/logging.py,sha256=YIfuDUEkmdn9cIRQ_Ec8rgXs1m5nOwDECtZqM4CBH5U,13093 -pip/_internal/utils/misc.py,sha256=cK17YkNfEccS9AuH6Xc9kYQxE0DPNgb-ULh8QgKPr8c,26195 -pip/_internal/utils/models.py,sha256=IA0hw_T4awQzui0kqfIEASm5yLtgZAB08ag59Nip5G8,1148 +pip/_internal/utils/misc.py,sha256=QQZWMJkKKADPSWQYmrwlasc8b03eCcghn0yDNprYgrI,28001 +pip/_internal/utils/models.py,sha256=HqiBVtTbW_b_Umvj2fjhDWOHo2RKhPwSz4iAYkQZ688,1201 pip/_internal/utils/packaging.py,sha256=VtiwcAAL7LBi7tGL2je7LeW4bE11KMHGCsJ1NZY5XtM,3035 +pip/_internal/utils/parallel.py,sha256=7az3aaTMCkqpaLFbpYYOvk0rj7Hu5YH1NPXXomqjgf4,3404 pip/_internal/utils/pkg_resources.py,sha256=ZX-k7V5q_aNWyDse92nN7orN1aCpRLsaxzpkBZ1XKzU,1254 pip/_internal/utils/setuptools_build.py,sha256=E1KswI7wfNnCDE5R6G8c9ZbByENpu7NqocjY26PCQDw,5058 -pip/_internal/utils/subprocess.py,sha256=vI2QWpNDqM-dkn-z8i1Yrfxnn5sYniPeWn6FhTxX4dY,9902 -pip/_internal/utils/temp_dir.py,sha256=H8yUBrRWqTM83cuUu7jVvw_xKL9eZtg_IIbXQtjMLlA,8185 +pip/_internal/utils/subprocess.py,sha256=UkPe89gcjxBMx73uutoeJXgD3kwdlL6YO16BkjDdVSI,9924 +pip/_internal/utils/temp_dir.py,sha256=blmG0jEvEgdxbYUt_V15bgcTIJIrxZwAw8QZlCTJYDE,8378 pip/_internal/utils/typing.py,sha256=xkYwOeHlf4zsHXBDC4310HtEqwhQcYXFPq2h35Tcrl0,1401 -pip/_internal/utils/unpacking.py,sha256=M944JTSiapBOSKLWu7lbawpVHSE7flfzZTEr3TAG7v8,9438 +pip/_internal/utils/unpacking.py,sha256=YFAckhqqvmehA8Kan5vd3b1kN_9TafqmOk4b-yz4fho,9488 pip/_internal/utils/urls.py,sha256=q2rw1kMiiig_XZcoyJSsWMJQqYw-2wUmrMoST4mCW_I,1527 -pip/_internal/utils/virtualenv.py,sha256=iVJ8ZlbNtGon6I4uZFsY2SidrUf1vt3YHrgS5CuU98w,3553 -pip/_internal/utils/wheel.py,sha256=ofsZEN35YhSxRYC4gfzpTtqa_UQ8GF1tl4jtyUdd0gU,7306 +pip/_internal/utils/virtualenv.py,sha256=fNGrRp-8QmNL5OzXajBd-z7PbwOsx1XY6G-AVMAhfso,3706 +pip/_internal/utils/wheel.py,sha256=wFzn3h8GqYvgsyWPZtUyn0Rb3MJzmtyP3owMOhKnmL0,7303 pip/_internal/vcs/__init__.py,sha256=viJxJRqRE_mVScum85bgQIXAd6o0ozFt18VpC-qIJrM,617 pip/_internal/vcs/__pycache__/__init__.cpython-38.pyc,, pip/_internal/vcs/__pycache__/bazaar.cpython-38.pyc,, @@ -262,13 +268,13 @@ pip/_internal/vcs/__pycache__/git.cpython-38.pyc,, pip/_internal/vcs/__pycache__/mercurial.cpython-38.pyc,, pip/_internal/vcs/__pycache__/subversion.cpython-38.pyc,, pip/_internal/vcs/__pycache__/versioncontrol.cpython-38.pyc,, -pip/_internal/vcs/bazaar.py,sha256=84q1-kj1_nJ9AMzMu8RmMp-riRZu81M7K9kowcYgi3U,3957 -pip/_internal/vcs/git.py,sha256=wlvvVT-hPRwCvkihEoOCZmkCzMzosmV43_DTPvEVA_M,14165 -pip/_internal/vcs/mercurial.py,sha256=wVdmoFH-RYoaxjtuAqw40b0daMPX-Fr_26W1ME_9HZU,5347 -pip/_internal/vcs/subversion.py,sha256=6shByxeASetbM7WCj6WNoPcuLfBK65DoOEqbkSiWPAI,12331 -pip/_internal/vcs/versioncontrol.py,sha256=RtSrHr96CynqXYBQIC61cVY_9C0e7hk8dXUV-BpHmpI,23591 -pip/_internal/wheel_builder.py,sha256=p9ZFawfCR1GXchTRY6oq7Qx5enLLjs2SouafNFNsAAE,9590 -pip/_vendor/__init__.py,sha256=Tfcbsek_rpFZWMnYp6vzGpWHsmiwBGYOmInUX1NGJp4,4788 +pip/_internal/vcs/bazaar.py,sha256=5rRR02uDZTLaxQT-R5Obd8FZDOMlShqYds-pwVSJJs8,3887 +pip/_internal/vcs/git.py,sha256=kvB729wrKY0OWMSgOS1pUly4LosZp8utrd3kOQsWalA,13985 +pip/_internal/vcs/mercurial.py,sha256=FzCGmYzVZvB-vyM73fKcQk2B4jMNXGnXlQ2bJ7nmglM,5162 +pip/_internal/vcs/subversion.py,sha256=rldcn9ZDt5twjNPzFn_FKRn4qdfkjlxHMJEsR2MFfoA,12399 +pip/_internal/vcs/versioncontrol.py,sha256=WpxeTRC0NoGB2uXJdmfq4pPxY-p7sk1rV_WkxMxgzQA,25966 +pip/_internal/wheel_builder.py,sha256=6w1VPXrpUvCCPlV0cI1wNaCqNz4laF6B6whvaxl9cns,9522 +pip/_vendor/__init__.py,sha256=CsxnpYPbi_2agrDI79iQrCmQeZRcwwIF0C6cm_1RynU,4588 pip/_vendor/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/__pycache__/appdirs.cpython-38.pyc,, pip/_vendor/__pycache__/contextlib2.cpython-38.pyc,, @@ -277,8 +283,7 @@ pip/_vendor/__pycache__/ipaddress.cpython-38.pyc,, pip/_vendor/__pycache__/pyparsing.cpython-38.pyc,, pip/_vendor/__pycache__/retrying.cpython-38.pyc,, pip/_vendor/__pycache__/six.cpython-38.pyc,, -pip/_vendor/__pycache__/toml.cpython-38.pyc,, -pip/_vendor/appdirs.py,sha256=pYg72GhKgkVzkPxZNFUSIzMF3tAPWBgIPoQE8jgVftg,25888 +pip/_vendor/appdirs.py,sha256=M6IYRJtdZgmSPCXCSMBRB0VT3P8MdFbWCDbSLrB2Ebg,25907 pip/_vendor/cachecontrol/__init__.py,sha256=pJtAaUxOsMPnytI1A3juAJkXYDr8krdSnsg4Yg3OBEg,302 pip/_vendor/cachecontrol/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-38.pyc,, @@ -305,13 +310,13 @@ pip/_vendor/cachecontrol/filewrapper.py,sha256=vACKO8Llzu_ZWyjV1Fxn1MA4TGU60N5N3 pip/_vendor/cachecontrol/heuristics.py,sha256=BFGHJ3yQcxvZizfo90LLZ04T_Z5XSCXvFotrp7Us0sc,4070 pip/_vendor/cachecontrol/serialize.py,sha256=vIa4jvq4x_KSOLdEIedoknX2aXYHQujLDFV4-F21Dno,7091 pip/_vendor/cachecontrol/wrapper.py,sha256=5LX0uJwkNQUtYSEw3aGmGu9WY8wGipd81mJ8lG0d0M4,690 -pip/_vendor/certifi/__init__.py,sha256=AOqspvggP_F62Q_4UmJAhx5rZkoRHoRYBE3SCnSJzAk,64 +pip/_vendor/certifi/__init__.py,sha256=u1E_DrSGj_nnEkK5VglvEqP8D80KpghLVWL0A_pq41A,62 pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255 pip/_vendor/certifi/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/certifi/__pycache__/__main__.cpython-38.pyc,, pip/_vendor/certifi/__pycache__/core.cpython-38.pyc,, -pip/_vendor/certifi/cacert.pem,sha256=hwBo73gFnF0BKiP3FQKfG32xkGDhxl4SwCQiH2rDKr0,284099 -pip/_vendor/certifi/core.py,sha256=c8hoNcYWz6rQo2VAM5d0mcEWiewCvQuOUbCwJz49vhQ,792 +pip/_vendor/certifi/cacert.pem,sha256=GhT24f0R7_9y4YY_hkXwkO7BthZhRGDCEMO348E9S14,282394 +pip/_vendor/certifi/core.py,sha256=jBrwKEWpG0IKcuozK0BQ2HHGp8adXAOyBPC7ddgR6vM,2315 pip/_vendor/chardet/__init__.py,sha256=YsP5wQlsHJ2auF1RZJfypiSrCA7_bQiRm3ES_NI76-Y,1559 pip/_vendor/chardet/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/chardet/__pycache__/big5freq.cpython-38.pyc,, @@ -407,7 +412,7 @@ pip/_vendor/colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiEr pip/_vendor/colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 pip/_vendor/colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 pip/_vendor/contextlib2.py,sha256=5HjGflUzwWAUfcILhSmC2GqvoYdZZzFzVfIDztHigUs,16915 -pip/_vendor/distlib/__init__.py,sha256=gzl1hjUXmDGrqRyU7ZLjBwJGAcMimQbrZ22XPVaKaRE,581 +pip/_vendor/distlib/__init__.py,sha256=3veAk2rPznOB2gsK6tjbbh0TQMmGE5P82eE9wXq6NIk,581 pip/_vendor/distlib/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/distlib/__pycache__/compat.cpython-38.pyc,, pip/_vendor/distlib/__pycache__/database.cpython-38.pyc,, @@ -428,28 +433,28 @@ pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-38.pyc,, pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-38.pyc,, pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-38.pyc,, pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971 -pip/_vendor/distlib/_backport/shutil.py,sha256=VW1t3uYqUjWZH7jV-6QiimLhnldoV5uIpH4EuiT1jfw,25647 +pip/_vendor/distlib/_backport/shutil.py,sha256=IX_G2NPqwecJibkIDje04bqu0xpHkfSQ2GaGdEVqM5Y,25707 pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 pip/_vendor/distlib/_backport/sysconfig.py,sha256=BQHFlb6pubCl_dvT1NjtzIthylofjKisox239stDg0U,26854 pip/_vendor/distlib/_backport/tarfile.py,sha256=Ihp7rXRcjbIKw8COm9wSePV9ARGXbSF9gGXAMn2Q-KU,92628 -pip/_vendor/distlib/compat.py,sha256=xdNZmqFN5HwF30HjRn5M415pcC2kgXRBXn767xS8v-M,41404 -pip/_vendor/distlib/database.py,sha256=fhNzEDtb4HXrpxKyQvhVzDXcOiJlzrOM--UYnvCeZrI,51045 +pip/_vendor/distlib/compat.py,sha256=ADA56xiAxar3mU6qemlBhNbsrFPosXRhO44RzsbJPqk,41408 +pip/_vendor/distlib/database.py,sha256=Kl0YvPQKc4OcpVi7k5cFziydM1xOK8iqdxLGXgbZHV4,51059 pip/_vendor/distlib/index.py,sha256=SXKzpQCERctxYDMp_OLee2f0J0e19ZhGdCIoMlUfUQM,21066 pip/_vendor/distlib/locators.py,sha256=c9E4cDEacJ_uKbuE5BqAVocoWp6rsuBGTkiNDQq3zV4,52100 pip/_vendor/distlib/manifest.py,sha256=nQEhYmgoreaBZzyFzwYsXxJARu3fo4EkunU163U16iE,14811 pip/_vendor/distlib/markers.py,sha256=6Ac3cCfFBERexiESWIOXmg-apIP8l2esafNSX3KMy-8,4387 -pip/_vendor/distlib/metadata.py,sha256=OhbCKmf5lswE8unWBopI1hj7tRpHp4ZbFvU4d6aAEMM,40234 +pip/_vendor/distlib/metadata.py,sha256=z2KPy3h3tcDnb9Xs7nAqQ5Oz0bqjWAUFmKWcFKRoodg,38962 pip/_vendor/distlib/resources.py,sha256=2FGv0ZHF14KXjLIlL0R991lyQQGcewOS4mJ-5n-JVnc,10766 -pip/_vendor/distlib/scripts.py,sha256=OAkEwxRvIzX-VSfhEttQEKJFVLA47gbW0OgQXJRs7OQ,16998 +pip/_vendor/distlib/scripts.py,sha256=_MAj3sMuv56kuM8FsiIWXqbT0gmumPGaOR_atOzn4a4,17180 pip/_vendor/distlib/t32.exe,sha256=NS3xBCVAld35JVFNmb-1QRyVtThukMrwZVeXn4LhaEQ,96768 pip/_vendor/distlib/t64.exe,sha256=oAqHes78rUWVM0OtVqIhUvequl_PKhAhXYQWnUf7zR0,105984 pip/_vendor/distlib/util.py,sha256=f2jZCPrcLCt6LcnC0gUy-Fur60tXD8reA7k4rDpHMDw,59845 pip/_vendor/distlib/version.py,sha256=_n7F6juvQGAcn769E_SHa7fOcf5ERlEVymJ_EjPRwGw,23391 pip/_vendor/distlib/w32.exe,sha256=lJtnZdeUxTZWya_EW5DZos_K5rswRECGspIl8ZJCIXs,90112 pip/_vendor/distlib/w64.exe,sha256=0aRzoN2BO9NWW4ENy4_4vHkHR4qZTFZNVSAJJYlODTI,99840 -pip/_vendor/distlib/wheel.py,sha256=bRtR5bNR_u_DwkwktN1bgZuwLVOJT1p_vNIUPyN8kJc,40452 +pip/_vendor/distlib/wheel.py,sha256=v6DnwTqhNHwrEVFr8_YeiTW6G4ftP_evsywNgrmdb2o,41144 pip/_vendor/distro.py,sha256=xxMIh2a3KmippeWEHzynTdHT3_jZM0o-pos0dAWJROM,43628 -pip/_vendor/html5lib/__init__.py,sha256=Ztrn7UvF-wIFAgRBBa0ML-Gu5AffH3BPX_INJx4SaBI,1162 +pip/_vendor/html5lib/__init__.py,sha256=BYzcKCqeEii52xDrqBFruhnmtmkiuHXFyFh-cglQ8mk,1160 pip/_vendor/html5lib/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-38.pyc,, pip/_vendor/html5lib/__pycache__/_inputstream.cpython-38.pyc,, @@ -458,19 +463,17 @@ pip/_vendor/html5lib/__pycache__/_utils.cpython-38.pyc,, pip/_vendor/html5lib/__pycache__/constants.cpython-38.pyc,, pip/_vendor/html5lib/__pycache__/html5parser.cpython-38.pyc,, pip/_vendor/html5lib/__pycache__/serializer.cpython-38.pyc,, -pip/_vendor/html5lib/_ihatexml.py,sha256=3LBtJMlzgwM8vpQiU1TvGmEEmNH72sV0yD8yS53y07A,16705 -pip/_vendor/html5lib/_inputstream.py,sha256=bPUWcAfJScK4xkjQQaG_HsI2BvEVbFvI0AsodDYPQj0,32552 -pip/_vendor/html5lib/_tokenizer.py,sha256=YAaOEBD6qc5ISq9Xt9Nif1OFgcybTTfMdwqBkZhpAq4,76580 -pip/_vendor/html5lib/_trie/__init__.py,sha256=8VR1bcgD2OpeS2XExpu5yBhP_Q1K-lwKbBKICBPf1kU,289 +pip/_vendor/html5lib/_ihatexml.py,sha256=ifOwF7pXqmyThIXc3boWc96s4MDezqRrRVp7FwDYUFs,16728 +pip/_vendor/html5lib/_inputstream.py,sha256=jErNASMlkgs7MpOM9Ve_VdLDJyFFweAjLuhVutZz33U,32353 +pip/_vendor/html5lib/_tokenizer.py,sha256=04mgA2sNTniutl2fxFv-ei5bns4iRaPxVXXHh_HrV_4,77040 +pip/_vendor/html5lib/_trie/__init__.py,sha256=nqfgO910329BEVJ5T4psVwQtjd2iJyEXQ2-X8c1YxwU,109 pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-38.pyc,, -pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-38.pyc,, pip/_vendor/html5lib/_trie/__pycache__/py.cpython-38.pyc,, pip/_vendor/html5lib/_trie/_base.py,sha256=CaybYyMro8uERQYjby2tTeSUatnWDfWroUN9N7ety5w,1013 -pip/_vendor/html5lib/_trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 pip/_vendor/html5lib/_trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 -pip/_vendor/html5lib/_utils.py,sha256=ismpASeqa2jqEPQjHUj8vReAf7yIoKnvLN5fuOw6nv0,4015 -pip/_vendor/html5lib/constants.py,sha256=4lmZWLtEPRLnl8NzftOoYTJdo6jpeMtP6dqQC0g_bWQ,83518 +pip/_vendor/html5lib/_utils.py,sha256=Dx9AKntksRjFT1veBj7I362pf5OgIaT0zglwq43RnfU,4931 +pip/_vendor/html5lib/constants.py,sha256=Ll-yzLU_jcjyAI_h57zkqZ7aQWE5t5xA4y_jQgoUUhw,83464 pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-38.pyc,, @@ -485,10 +488,10 @@ pip/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVr pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945 pip/_vendor/html5lib/filters/lint.py,sha256=jk6q56xY0ojiYfvpdP-OZSm9eTqcAdRqhCoPItemPYA,3643 pip/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588 -pip/_vendor/html5lib/filters/sanitizer.py,sha256=4ON02KNjuqda1lCw5_JCUZxb0BzWR5M7ON84dtJ7dm0,26248 +pip/_vendor/html5lib/filters/sanitizer.py,sha256=m6oGmkBhkGAnn2nV6D4hE78SCZ6WEnK9rKdZB3uXBIc,26897 pip/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214 -pip/_vendor/html5lib/html5parser.py,sha256=g5g2ezkusHxhi7b23vK_-d6K6BfIJRbqIQmvQ9z4EgI,118963 -pip/_vendor/html5lib/serializer.py,sha256=yfcfBHse2wDs6ojxn-kieJjLT5s1ipilQJ0gL3-rJis,15758 +pip/_vendor/html5lib/html5parser.py,sha256=anr-aXre_ImfrkQ35c_rftKXxC80vJCREKe06Tq15HA,117186 +pip/_vendor/html5lib/serializer.py,sha256=_PpvcZF07cwE7xr9uKkZqh5f4UEaI8ltCU2xPJzaTpk,15759 pip/_vendor/html5lib/treeadapters/__init__.py,sha256=A0rY5gXIe4bJOiSGRO_j_tFhngRBO8QZPzPtPw5dFzo,679 pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-38.pyc,, @@ -501,11 +504,11 @@ pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-38.pyc,, pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-38.pyc,, pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-38.pyc,, pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-38.pyc,, -pip/_vendor/html5lib/treebuilders/base.py,sha256=wQGp5yy22TNG8tJ6aREe4UUeTR7A99dEz0BXVaedWb4,14579 +pip/_vendor/html5lib/treebuilders/base.py,sha256=z-o51vt9r_l2IDG5IioTOKGzZne4Fy3_Fc-7ztrOh4I,14565 pip/_vendor/html5lib/treebuilders/dom.py,sha256=22whb0C71zXIsai5mamg6qzBEiigcBIvaDy4Asw3at0,8925 -pip/_vendor/html5lib/treebuilders/etree.py,sha256=aqIBOGj_dFYqBURIcTegGNBhAIJOw5iFDHb4jrkYH-8,12764 -pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9V0dXxbJYYq-Skgb5-_OL2NkVYpjioEb4CHajo0e9yI,14122 -pip/_vendor/html5lib/treewalkers/__init__.py,sha256=yhXxHpjlSqfQyUag3v8-vWjMPriFBU8YRAPNpDgBTn8,5714 +pip/_vendor/html5lib/treebuilders/etree.py,sha256=w5ZFpKk6bAxnrwD2_BrF5EVC7vzz0L3LMi9Sxrbc_8w,12836 +pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9gqDjs-IxsPhBYa5cpvv2FZ1KZlG83Giusy2lFmvIkE,14766 +pip/_vendor/html5lib/treewalkers/__init__.py,sha256=OBPtc1TU5mGyy18QDMxKEyYEz0wxFUUNj5v0-XgmYhY,5719 pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-38.pyc,, pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-38.pyc,, @@ -514,8 +517,8 @@ pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-38.pyc,, pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-38.pyc,, pip/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476 pip/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413 -pip/_vendor/html5lib/treewalkers/etree.py,sha256=sz1o6mmE93NQ53qJFDO7HKyDtuwgK-Ay3qSFZPC6u00,4550 -pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=sY6wfRshWTllu6n48TPWpKsQRPp-0CQrT0hj_AdzHSU,6309 +pip/_vendor/html5lib/treewalkers/etree.py,sha256=xo1L5m9VtkfpFJK0pFmkLVajhqYYVisVZn3k9kYpPkI,4551 +pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=_b0LAVWLcVu9WaU_-w3D8f0IRSpCbjf667V-3NRdhTw,6357 pip/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309 pip/_vendor/idna/__init__.py,sha256=9Nt7xpyet3DmOrPUGooDdAwmHZZu1qUAy2EaJ93kGiQ,58 pip/_vendor/idna/__pycache__/__init__.cpython-38.pyc,, @@ -528,11 +531,11 @@ pip/_vendor/idna/__pycache__/package_data.cpython-38.pyc,, pip/_vendor/idna/__pycache__/uts46data.cpython-38.pyc,, pip/_vendor/idna/codec.py,sha256=lvYb7yu7PhAqFaAIAdWcwgaWI2UmgseUua-1c0AsG0A,3299 pip/_vendor/idna/compat.py,sha256=R-h29D-6mrnJzbXxymrWUW7iZUvy-26TQwZ0ij57i4U,232 -pip/_vendor/idna/core.py,sha256=Hy1RkJrrIQWW8kicqZ8vQT_GreYlAhkfopjESSzL3wk,11844 -pip/_vendor/idna/idnadata.py,sha256=p1_KeD9BqT-sDGqMcGxhBWAOrYNrPxj5YvHya0ImFbU,41201 +pip/_vendor/idna/core.py,sha256=jCoaLb3bA2tS_DDx9PpGuNTEZZN2jAzB369aP-IHYRE,11951 +pip/_vendor/idna/idnadata.py,sha256=gmzFwZWjdms3kKZ_M_vwz7-LP_SCgYfSeE03B21Qpsk,42350 pip/_vendor/idna/intranges.py,sha256=TY1lpxZIQWEP6tNqjZkFA5hgoMWOj1OBmnUG8ihT87E,1749 -pip/_vendor/idna/package_data.py,sha256=IjspS_rQQ_0HCGc0CaNhn3NXl3ohvRg7-_P0gAaSc-o,21 -pip/_vendor/idna/uts46data.py,sha256=w9d1B5OESLSgr2tMx0svwoPBi0Qj0_7HRyL1Vq5axwg,201192 +pip/_vendor/idna/package_data.py,sha256=bxBjpLnE06_1jSYKEy5svOMu1zM3OMztXVUb1tPlcp0,22 +pip/_vendor/idna/uts46data.py,sha256=lMdw2zdjkH1JUWXPPEfFUSYT3Fyj60bBmfLvvy5m7ko,202084 pip/_vendor/ipaddress.py,sha256=-0RmurI31XgAaN20WCi0zrcuoat90nNA70_6yGlx2PU,79875 pip/_vendor/msgpack/__init__.py,sha256=2gJwcsTIaAtCM0GMi2rU-_Y6kILeeQuqRkrQ22jSANc,1118 pip/_vendor/msgpack/__pycache__/__init__.cpython-38.pyc,, @@ -544,7 +547,7 @@ pip/_vendor/msgpack/_version.py,sha256=hu7lzmZ_ClOaOOmRsWb4xomhzQ4UIsLsvv8KY6Uys pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081 pip/_vendor/msgpack/ext.py,sha256=nV19BzE9Be8SJHrxxYJHFbvEHJaXcP3avRkHVp5wovM,6034 pip/_vendor/msgpack/fallback.py,sha256=Z8V3iYUUPqKVy4WWTk64Vq3G0PylQIOmlWvgnMhmkdU,37133 -pip/_vendor/packaging/__about__.py,sha256=y-K51xPSysxvOfTjAb074yqOZfDeX5qSID0ZEbEb9cE,744 +pip/_vendor/packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736 pip/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 pip/_vendor/packaging/__pycache__/__about__.cpython-38.pyc,, pip/_vendor/packaging/__pycache__/__init__.cpython-38.pyc,, @@ -557,15 +560,15 @@ pip/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc,, pip/_vendor/packaging/__pycache__/tags.cpython-38.pyc,, pip/_vendor/packaging/__pycache__/utils.cpython-38.pyc,, pip/_vendor/packaging/__pycache__/version.cpython-38.pyc,, -pip/_vendor/packaging/_compat.py,sha256=Z-PwchK0cREbaRGF5MZP8LEv8JkC-qydn2FRrtjeixk,1138 +pip/_vendor/packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128 pip/_vendor/packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 -pip/_vendor/packaging/_typing.py,sha256=-cq_iNeveAWCVoseVvqmknWLbvZ_i9g7BeZBo0ShtHg,1449 -pip/_vendor/packaging/markers.py,sha256=yap5bk3c8QyPuGtiVbQSYhN70bxWj1nLDv2ZuaCLq7g,9501 -pip/_vendor/packaging/requirements.py,sha256=G43p2ylM_REg87RLG9JybjbdwfaPyzaKYRtllRfNdrM,4913 -pip/_vendor/packaging/specifiers.py,sha256=Nz8bnFp53cQInmRGZy50QXlIi2tkDXMfRuGyGps2IRE,31314 -pip/_vendor/packaging/tags.py,sha256=SCrw-jC3h0ymam6QXDX5ZqgvRcMNq_cQD55gFnT56Xg,23704 -pip/_vendor/packaging/utils.py,sha256=v5Wk8B7gUL13Rzed6NNhCZlutPQT7jNV-7hr-WOtacU,1700 -pip/_vendor/packaging/version.py,sha256=qRdNN0_XuPFOJ3fut8ehzxJrNYtBzqF8ZtagEvgNUUM,15480 +pip/_vendor/packaging/_typing.py,sha256=VgA0AAvsc97KB5nF89zoudOyCMEsV7FlaXzZbYqEkzA,1824 +pip/_vendor/packaging/markers.py,sha256=V_RdoQqOUbSfy7y9o2vRk7BkzAh3yneC82cuWpKrqOg,9491 +pip/_vendor/packaging/requirements.py,sha256=F93hkn7i8NKRZP-FtdTIlhz1PUsRjhe6eRbsBXX0Uh4,4903 +pip/_vendor/packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944 +pip/_vendor/packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067 +pip/_vendor/packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811 +pip/_vendor/packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470 pip/_vendor/pep517/__init__.py,sha256=r5uA106NGJa3slspaD2m32aFpFUiZX-mZ9vIlzAEOp4,84 pip/_vendor/pep517/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/pep517/__pycache__/_in_process.cpython-38.pyc,, @@ -599,7 +602,7 @@ pip/_vendor/progress/bar.py,sha256=QuDuVNcmXgpxtNtxO0Fq72xKigxABaVmxYGBw4J3Z_E,2 pip/_vendor/progress/counter.py,sha256=MznyBrvPWrOlGe4MZAlGUb9q3aODe6_aNYeAE_VNoYA,1372 pip/_vendor/progress/spinner.py,sha256=k8JbDW94T0-WXuXfxZIFhdoNPYp3jfnpXqBnfRv5fGs,1380 pip/_vendor/pyparsing.py,sha256=J1b4z3S_KwyJW7hKGnoN-hXW9pgMIzIP6QThyY5yJq4,273394 -pip/_vendor/requests/__init__.py,sha256=DoS7sn6SOs-Vmi3IHdJFfM1RmlONZHmEKO3DGvyWsnY,4080 +pip/_vendor/requests/__init__.py,sha256=orzv4-1uejMDc2v3LnTVneINGXiwqXSfrASoFBsYblE,4465 pip/_vendor/requests/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/requests/__pycache__/__version__.cpython-38.pyc,, pip/_vendor/requests/__pycache__/_internal_utils.cpython-38.pyc,, @@ -618,7 +621,7 @@ pip/_vendor/requests/__pycache__/sessions.cpython-38.pyc,, pip/_vendor/requests/__pycache__/status_codes.cpython-38.pyc,, pip/_vendor/requests/__pycache__/structures.cpython-38.pyc,, pip/_vendor/requests/__pycache__/utils.cpython-38.pyc,, -pip/_vendor/requests/__version__.py,sha256=dpcXABdGo9y3UFFKFU_Wu_YHSa7TBXxCghOju7S8IYs,441 +pip/_vendor/requests/__version__.py,sha256=Xwky1FMlMkJJGidBM50JC7FKcosWzkjIW-WhQGrBdFM,441 pip/_vendor/requests/_internal_utils.py,sha256=Zx3PnEUccyfsB-ie11nZVAW8qClJy0gx1qNME7rgT18,1096 pip/_vendor/requests/adapters.py,sha256=e-bmKEApNVqFdylxuMJJfiaHdlmS_zhWhIMEzlHvGuc,21548 pip/_vendor/requests/api.py,sha256=PlHM-HT3PQ5lyufoeGmV-nJxRi7UnUyGVh7OV7B9XV4,6496 @@ -626,39 +629,44 @@ pip/_vendor/requests/auth.py,sha256=OMoJIVKyRLy9THr91y8rxysZuclwPB-K1Xg1zBomUhQ, pip/_vendor/requests/certs.py,sha256=nXRVq9DtGmv_1AYbwjTu9UrgAcdJv05ZvkNeaoLOZxY,465 pip/_vendor/requests/compat.py,sha256=LQWuCR4qXk6w7-qQopXyz0WNHUdAD40k0mKnaAEf1-g,2045 pip/_vendor/requests/cookies.py,sha256=Y-bKX6TvW3FnYlE6Au0SXtVVWcaNdFvuAwQxw-G0iTI,18430 -pip/_vendor/requests/exceptions.py,sha256=-mLam3TAx80V09EaH3H-ZxR61eAVuLRZ8zgBBSLjK44,3197 +pip/_vendor/requests/exceptions.py,sha256=d9fJJw8YFBB9VzG9qhvxLuOx6be3c_Dwbck-dVUEAcs,3173 pip/_vendor/requests/help.py,sha256=SJPVcoXeo7KfK4AxJN5eFVQCjr0im87tU2n7ubLsksU,3578 pip/_vendor/requests/hooks.py,sha256=QReGyy0bRcr5rkwCuObNakbYsc7EkiKeBwG4qHekr2Q,757 -pip/_vendor/requests/models.py,sha256=P7sUBpjV7iIzjpf9rYVzHaOXlxlzJk5ikFP_Nsg6zAU,34278 +pip/_vendor/requests/models.py,sha256=_tKIbrscbGvaTdX1UHCwRaiYmPF9VBIuBeydr4Qx1Tg,34287 pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 -pip/_vendor/requests/sessions.py,sha256=Ju8VnlWZPU_Xr-cMjKXNbs_l2cyanr3Dm9c7fLxprQI,29265 +pip/_vendor/requests/sessions.py,sha256=OBtwQs1vjkB1xamFdi_p5y8BVeX16BJoQcwSwx_Y3fI,29316 pip/_vendor/requests/status_codes.py,sha256=gT79Pbs_cQjBgp-fvrUgg1dn2DQO32bDj4TInjnMPSc,4188 pip/_vendor/requests/structures.py,sha256=msAtr9mq1JxHd-JRyiILfdFlpbJwvvFuP3rfUQT_QxE,3005 pip/_vendor/requests/utils.py,sha256=VBs99cvV8Z29WGXeWZqHzZ80_nu1AwwjYzJfe0wQIvs,30176 -pip/_vendor/resolvelib/__init__.py,sha256=aBndiGQ3I68Ezdv0fMPQ9ek6ScvwpuQRimxn6wp7pJ4,537 +pip/_vendor/resolvelib/__init__.py,sha256=sqMOy4CbVJQiaG9bCPj0oAntGAVy-RWdPfVaC9XDIEQ,537 pip/_vendor/resolvelib/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/resolvelib/__pycache__/providers.cpython-38.pyc,, pip/_vendor/resolvelib/__pycache__/reporters.cpython-38.pyc,, pip/_vendor/resolvelib/__pycache__/resolvers.cpython-38.pyc,, pip/_vendor/resolvelib/__pycache__/structs.cpython-38.pyc,, -pip/_vendor/resolvelib/providers.py,sha256=1JCBbBV0E_Q1o0XOAeAdr2YaKU_zHxO7i_BBLqCLhUc,4913 -pip/_vendor/resolvelib/reporters.py,sha256=5gXUn3hRjA4UomD3HHZGreBp23aSjMHgBrC3OgYSpTY,1094 -pip/_vendor/resolvelib/resolvers.py,sha256=nTTfBBIMsoxGhqUAyh8GbmR-si_TijpY67wD7aUS264,14481 +pip/_vendor/resolvelib/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-38.pyc,, +pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-38.pyc,, +pip/_vendor/resolvelib/compat/collections_abc.py,sha256=mtTkpr3Gf3OGvU1PD8YuvrJRhVbioxV82T-niFPoX3o,127 +pip/_vendor/resolvelib/providers.py,sha256=TZDCmL-Ic-R5JRIZY8G4FLG5xB2343B0DfuK7aw2Yqw,4547 +pip/_vendor/resolvelib/reporters.py,sha256=ZPSJnVfK8WvXTbX8jE0Nren0-_Hg9ym4epCUPtU8Y0U,1405 +pip/_vendor/resolvelib/resolvers.py,sha256=lQTGcc-2fgHbmdiLzeNDUxVmGc5ZFjkAL6JrVqnqJIw,15018 pip/_vendor/resolvelib/structs.py,sha256=yrdhd-n7DercimPGclXe20rgqhlxw8PnxC0wmcXO19Y,2016 pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972 -pip/_vendor/six.py,sha256=Q6WvEXZ1DGEASAo3CGNCJkKv2tPy8xkSmK-VHE9PYIA,34074 -pip/_vendor/toml.py,sha256=D8pTQbgSGge2nDdpsAk8_C7UGJeva0qFq2tGGNaE70Q,35675 -pip/_vendor/toml/__init__.py,sha256=CwtvzaEThoIbqNwJJB8BhHoA5clb2_x8V42UF6JAysA,527 +pip/_vendor/six.py,sha256=U4Z_yv534W5CNyjY9i8V1OXY2SjAny8y2L5vDLhhThM,34159 +pip/_vendor/toml/__init__.py,sha256=rJ1pu933HgUtyeeNiusoPd5jJOPNhaKHhSSld3o8AQo,747 pip/_vendor/toml/__pycache__/__init__.cpython-38.pyc,, +pip/_vendor/toml/__pycache__/common.cpython-38.pyc,, pip/_vendor/toml/__pycache__/decoder.cpython-38.pyc,, pip/_vendor/toml/__pycache__/encoder.cpython-38.pyc,, pip/_vendor/toml/__pycache__/ordered.cpython-38.pyc,, pip/_vendor/toml/__pycache__/tz.cpython-38.pyc,, -pip/_vendor/toml/decoder.py,sha256=8uIcWLLyMFwBGT3fiph98KDx8uF_V2Pf84Qi43pqNf0,35067 -pip/_vendor/toml/encoder.py,sha256=Nw1wrNgcUgod1XbOXwxFSDGqO5blgOi07Lp_6kStTgc,8128 +pip/_vendor/toml/common.py,sha256=ViBccAduP6eZNJAb1POhRhjOAi56TDsNgWJ1TjgXAug,242 +pip/_vendor/toml/decoder.py,sha256=atpXmyFCzNGiqhkcYLySBuJQkPeSHDzBz47sEaX1amw,38696 +pip/_vendor/toml/encoder.py,sha256=fPqLyFdPAam17X9SELz2TMp9affkfHCmgWZxRKcmzhY,9955 pip/_vendor/toml/ordered.py,sha256=UWt5Eka90IWVBYdvLgY5PXnkBcVYpHjnw9T67rM85T8,378 pip/_vendor/toml/tz.py,sha256=DrAgI3wZxZiGcLuV_l8ueA_nPrYoxQ3hZA9tJSjWRsQ,618 -pip/_vendor/urllib3/__init__.py,sha256=oVOeFuNyEg33ShIefD4qSeGvbb-aJ3otjcdX2iswkLM,2683 +pip/_vendor/urllib3/__init__.py,sha256=rdFZCO1L7e8861ZTvo8AiSKwxCe9SnWQUQwJ599YV9c,2683 pip/_vendor/urllib3/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/urllib3/__pycache__/_collections.cpython-38.pyc,, pip/_vendor/urllib3/__pycache__/connection.cpython-38.pyc,, @@ -670,8 +678,8 @@ pip/_vendor/urllib3/__pycache__/poolmanager.cpython-38.pyc,, pip/_vendor/urllib3/__pycache__/request.cpython-38.pyc,, pip/_vendor/urllib3/__pycache__/response.cpython-38.pyc,, pip/_vendor/urllib3/_collections.py,sha256=GouVsNzwg6jADZTmimMI6oqmwKSswnMo9dh5tGNVWO4,10792 -pip/_vendor/urllib3/connection.py,sha256=wHpV1S60bvXWnqEDtdzmHiY9NwIiKeFwQI5VvX7AtqM,14026 -pip/_vendor/urllib3/connectionpool.py,sha256=jg_yTHo2B3Zyo4urYVecyg-34v3hxUrH9B401-c4A9w,36513 +pip/_vendor/urllib3/connection.py,sha256=Fln8a_bkegdNMkFoSOwyI0PJvL1OqzVUO6ifihKOTpc,14461 +pip/_vendor/urllib3/connectionpool.py,sha256=egdaX-Db_LVXifDxv3JY0dHIpQqDv0wC0_9Eeh8FkPM,35725 pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-38.pyc,, @@ -689,10 +697,10 @@ pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=mullWYFaghBdRWla pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=V7GnujxnWZh2N2sMsV5N4d9Imymokkm3zBwgt77_bSE,11956 pip/_vendor/urllib3/contrib/appengine.py,sha256=gfdK4T7CRin7v9HRhHDbDh-Hbk66hHDWeoz7nV3PJo8,11034 pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=a402AwGN_Ll3N-4ur_AS6UrU-ycUtlnYqoBF76lORg8,4160 -pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=w35mWy_1POZUsbOhurVb_zhf0C1Jkd79AFlucLs6KuQ,16440 -pip/_vendor/urllib3/contrib/securetransport.py,sha256=iKzVUAxKnChsADR5YMwc05oEixXDzAk0xPU0g-rc2z8,32275 +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=9gm5kpC0ScbDCWobeCrh5LDqS8HgU8FNhmk5v8qQ5Bs,16582 +pip/_vendor/urllib3/contrib/securetransport.py,sha256=vBDFjSnH2gWa-ztMKVaiwW46K1mlDZKqvo_VAonfdcY,32401 pip/_vendor/urllib3/contrib/socks.py,sha256=nzDMgDIFJWVubKHqvIn2-SKCO91hhJInP92WgHChGzA,7036 -pip/_vendor/urllib3/exceptions.py,sha256=P3e-p9_LScyIxX7FoR3wU0A6hZmDqFAVCz2wgI3D0lM,6607 +pip/_vendor/urllib3/exceptions.py,sha256=D2Jvab7M7m_n0rnmBmq481paoVT32VvVeB6VeQM0y-w,7172 pip/_vendor/urllib3/fields.py,sha256=kroD76QK-GdHHW7f_AUN4XxDC3OQPI2FFrS9eSL4BCs,8553 pip/_vendor/urllib3/filepost.py,sha256=vj0qbrpT1AFzvvW4SuC8M5kJiw7wftHcSr-7b8UpPpw,2440 pip/_vendor/urllib3/packages/__init__.py,sha256=h4BLhD4tLaBx1adaDtKXfupsgqY0wWLXb_f1_yVlV6A,108 @@ -707,9 +715,9 @@ pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py,sha256=ywgKMtfHi1-Dr pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-38.pyc,, pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=rvQDQviqQLtPJB6MfEgABnBFj3nXft7ZJ3Dx-BC0AQY,5696 -pip/_vendor/urllib3/poolmanager.py,sha256=JYUyBUN3IiEknUdjZ7VJrpCQr6SP7vi0WwSndrn8XpE,17053 +pip/_vendor/urllib3/poolmanager.py,sha256=iWEAIGrVNGoOmQyfiFwCqG-IyYy6GIQ-jJ9QCsX9li4,17861 pip/_vendor/urllib3/request.py,sha256=hhoHvEEatyd9Tn5EbGjQ0emn-ENMCyY591yNWTneINA,6018 -pip/_vendor/urllib3/response.py,sha256=5OVDLR6ss88mak30ZhDaSqHxXbbAQ7CIUrktI7B5aPA,27833 +pip/_vendor/urllib3/response.py,sha256=eo1Sfkn2x44FtjgP3qwwDsG9ak84spQAxEGy7Ovd4Pc,28221 pip/_vendor/urllib3/util/__init__.py,sha256=bWNaav_OT-1L7-sxm59cGb59rDORlbhb_4noduM5m0U,1038 pip/_vendor/urllib3/util/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/urllib3/util/__pycache__/connection.cpython-38.pyc,, @@ -725,12 +733,12 @@ pip/_vendor/urllib3/util/connection.py,sha256=NsxUAKQ98GKywta--zg57CdVpeTCI6N-GE pip/_vendor/urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497 pip/_vendor/urllib3/util/request.py,sha256=C-6-AWffxZG03AdRGoY59uqsn4CVItKU6gjxz7Hc3Mc,3815 pip/_vendor/urllib3/util/response.py,sha256=_WbTQr8xRQuJuY2rTIZxVdJD6mnEOtQupjaK_bF_Vj8,2573 -pip/_vendor/urllib3/util/retry.py,sha256=Ui74h44gLIIWkAxT9SK3A2mEvu55-odWgJMw3LiUNGk,15450 -pip/_vendor/urllib3/util/ssl_.py,sha256=W52OyrqLO1IQ5kUgzM8fLAO-EgJxuftd5nhcTOD54ew,14167 -pip/_vendor/urllib3/util/timeout.py,sha256=bCtaS_xVKaTDJ5VMlroXBfCnPUDNVGZqik7-z83issg,9871 -pip/_vendor/urllib3/util/url.py,sha256=6cDyd46labP8STbF0vieWygu2xVhKkgcF1h3tc6HYno,13979 +pip/_vendor/urllib3/util/retry.py,sha256=3wbv7SdzYNOxPcBiFkPCubTbK1_6vWSepznOXirhUfA,15543 +pip/_vendor/urllib3/util/ssl_.py,sha256=N7gqt2iqzKBsWGmc61YeKNSPri6Ns2iZ_MD5hV2y8tU,14523 +pip/_vendor/urllib3/util/timeout.py,sha256=3qawUo-TZq4q7tyeRToMIOdNGEOBjOOQVq7nHnLryP4,9947 +pip/_vendor/urllib3/util/url.py,sha256=S4YyAwWKJPjFFECC7l9Vp9EKqRH1XAb-uQFANn1Tak0,13981 pip/_vendor/urllib3/util/wait.py,sha256=k46KzqIYu3Vnzla5YW3EvtInNlU_QycFqQAghIOxoAg,5406 -pip/_vendor/vendor.txt,sha256=ZewT4rljE_Ct9eJsAk0HhDlKSHaguTqeBSGlF1zIgss,440 +pip/_vendor/vendor.txt,sha256=bWUiaRjMJhuUsqFZHEJkBH_6lJ_Avl9cOyszcI74IHs,437 pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 pip/_vendor/webencodings/__pycache__/__init__.cpython-38.pyc,, pip/_vendor/webencodings/__pycache__/labels.cpython-38.pyc,, diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/WHEEL b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/WHEEL similarity index 100% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/WHEEL rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/WHEEL diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/entry_points.txt b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/entry_points.txt similarity index 100% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/entry_points.txt rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/entry_points.txt diff --git a/venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/top_level.txt b/venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/top_level.txt similarity index 100% rename from venv/lib/python3.8/site-packages/pip-20.1.1.dist-info/top_level.txt rename to venv/lib/python3.8/site-packages/pip-20.2.2.dist-info/top_level.txt diff --git a/venv/lib/python3.8/site-packages/pip/__init__.py b/venv/lib/python3.8/site-packages/pip/__init__.py index 3dcf3a9..611753f 100644 --- a/venv/lib/python3.8/site-packages/pip/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/__init__.py @@ -4,7 +4,7 @@ if MYPY_CHECK_RUNNING: from typing import List, Optional -__version__ = "20.1.1" +__version__ = "20.2.2" def main(args=None): diff --git a/venv/lib/python3.8/site-packages/pip/_internal/build_env.py b/venv/lib/python3.8/site-packages/pip/_internal/build_env.py index b8f005f..28d1ad6 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/build_env.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/build_env.py @@ -1,10 +1,6 @@ """Build Environment used for isolation during sdist building """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - import logging import os import sys @@ -22,7 +18,8 @@ from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Tuple, Set, Iterable, Optional, List + from types import TracebackType + from typing import Tuple, Set, Iterable, Optional, List, Type from pip._internal.index.package_finder import PackageFinder logger = logging.getLogger(__name__) @@ -110,6 +107,7 @@ class BuildEnvironment(object): ).format(system_sites=system_sites, lib_dirs=self._lib_dirs)) def __enter__(self): + # type: () -> None self._save_env = { name: os.environ.get(name, None) for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH') @@ -128,7 +126,13 @@ class BuildEnvironment(object): 'PYTHONPATH': os.pathsep.join(pythonpath), }) - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None for varname, old_value in self._save_env.items(): if old_value is None: os.environ.pop(varname, None) @@ -159,7 +163,7 @@ class BuildEnvironment(object): finder, # type: PackageFinder requirements, # type: Iterable[str] prefix_as_string, # type: str - message # type: Optional[str] + message # type: str ): # type: (...) -> None prefix = self._prefixes[prefix_as_string] @@ -193,6 +197,8 @@ class BuildEnvironment(object): args.extend(['--trusted-host', host]) if finder.allow_all_prereleases: args.append('--pre') + if finder.prefer_binary: + args.append('--prefer-binary') args.append('--') args.extend(requirements) with open_spinner(message) as spinner: @@ -204,16 +210,32 @@ class NoOpBuildEnvironment(BuildEnvironment): """ def __init__(self): + # type: () -> None pass def __enter__(self): + # type: () -> None pass - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None pass def cleanup(self): + # type: () -> None pass - def install_requirements(self, finder, requirements, prefix, message): + def install_requirements( + self, + finder, # type: PackageFinder + requirements, # type: Iterable[str] + prefix_as_string, # type: str + message # type: str + ): + # type: (...) -> None raise NotImplementedError() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/cache.py index b534f0c..07db948 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/cache.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/cache.py @@ -1,9 +1,6 @@ """Cache Management """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import hashlib import json import logging @@ -122,7 +119,7 @@ class Cache(object): return parts def _get_candidates(self, link, canonical_package_name): - # type: (Link, Optional[str]) -> List[Any] + # type: (Link, str) -> List[Any] can_not_cache = ( not self.cache_dir or not canonical_package_name or @@ -185,6 +182,7 @@ class SimpleWheelCache(Cache): def get_path_for_link_legacy(self, link): # type: (Link) -> str parts = self._get_cache_path_parts_legacy(link) + assert self.cache_dir return os.path.join(self.cache_dir, "wheels", *parts) def get_path_for_link(self, link): @@ -204,7 +202,7 @@ class SimpleWheelCache(Cache): :param link: The link of the sdist for which this will cache wheels. """ parts = self._get_cache_path_parts(link) - + assert self.cache_dir # Store wheels within the root cache_dir return os.path.join(self.cache_dir, "wheels", *parts) @@ -230,10 +228,9 @@ class SimpleWheelCache(Cache): continue if canonicalize_name(wheel.name) != canonical_package_name: logger.debug( - "Ignoring cached wheel {} for {} as it " - "does not match the expected distribution name {}.".format( - wheel_name, link, package_name - ) + "Ignoring cached wheel %s for %s as it " + "does not match the expected distribution name %s.", + wheel_name, link, package_name, ) continue if not wheel.supported(supported_tags): diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py index 1fa5ba0..c3b6a85 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py @@ -19,7 +19,6 @@ from pip._internal.cli.parser import ( from pip._internal.cli.status_codes import ( ERROR, PREVIOUS_BUILD_DIR_ERROR, - SUCCESS, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, ) @@ -27,7 +26,9 @@ from pip._internal.exceptions import ( BadCommand, CommandError, InstallationError, + NetworkConnectionError, PreviousBuildDirError, + SubProcessError, UninstallationError, ) from pip._internal.utils.deprecation import deprecated @@ -88,6 +89,12 @@ class Command(CommandContextMixIn): ) self.parser.add_option_group(gen_opts) + self.add_options() + + def add_options(self): + # type: () -> None + pass + def handle_pip_version_check(self, options): # type: (Values) -> None """ @@ -99,7 +106,7 @@ class Command(CommandContextMixIn): assert not hasattr(options, 'no_index') def run(self, options, args): - # type: (Values, List[Any]) -> Any + # type: (Values, List[Any]) -> int raise NotImplementedError def parse_args(self, args): @@ -142,7 +149,7 @@ class Command(CommandContextMixIn): ): message = ( "pip 21.0 will drop support for Python 2.7 in January 2021. " - "More details about Python 2 support in pip, can be found at " + "More details about Python 2 support in pip can be found at " "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa ) if platform.python_implementation() == "CPython": @@ -184,18 +191,38 @@ class Command(CommandContextMixIn): ) options.cache_dir = None + if getattr(options, "build_dir", None): + deprecated( + reason=( + "The -b/--build/--build-dir/--build-directory " + "option is deprecated." + ), + replacement=( + "use the TMPDIR/TEMP/TMP environment variable, " + "possibly combined with --no-clean" + ), + gone_in="20.3", + issue=8333, + ) + + if 'resolver' in options.unstable_features: + logger.critical( + "--unstable-feature=resolver is no longer supported, and " + "has been replaced with --use-feature=2020-resolver instead." + ) + sys.exit(ERROR) + try: status = self.run(options, args) - # FIXME: all commands should return an exit status - # and when it is done, isinstance is not needed anymore - if isinstance(status, int): - return status + assert isinstance(status, int) + return status except PreviousBuildDirError as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return PREVIOUS_BUILD_DIR_ERROR - except (InstallationError, UninstallationError, BadCommand) as exc: + except (InstallationError, UninstallationError, BadCommand, + SubProcessError, NetworkConnectionError) as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) @@ -224,5 +251,3 @@ class Command(CommandContextMixIn): return UNKNOWN_ERROR finally: self.handle_pip_version_check(options) - - return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py index 5b5647d..ed42c5f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py @@ -12,7 +12,6 @@ pass on state. To be consistent, all options will follow this design. from __future__ import absolute_import -import logging import os import textwrap import warnings @@ -35,8 +34,6 @@ if MYPY_CHECK_RUNNING: from optparse import OptionParser, Values from pip._internal.cli.parser import ConfigOptionParser -logger = logging.getLogger(__name__) - def raise_option_error(parser, option, msg): # type: (OptionParser, Option, str) -> None @@ -243,7 +240,7 @@ no_input = partial( dest='no_input', action='store_true', default=False, - help=SUPPRESS_HELP + help="Disable prompting for input." ) # type: Callable[..., Option] proxy = partial( @@ -702,7 +699,8 @@ build_dir = partial( metavar='dir', action='callback', callback=_handle_build_dir, - help='Directory to unpack packages into and build in. Note that ' + help='(DEPRECATED) ' + 'Directory to unpack packages into and build in. Note that ' 'an initial build still takes place in a temporary directory. ' 'The location of temporary directories can be controlled by setting ' 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' @@ -824,16 +822,6 @@ disable_pip_version_check = partial( ) # type: Callable[..., Option] -# Deprecated, Remove later -always_unzip = partial( - Option, - '-Z', '--always-unzip', - dest='always_unzip', - action='store_true', - help=SUPPRESS_HELP, -) # type: Callable[..., Option] - - def _handle_merge_hash(option, opt_str, value, parser): # type: (Option, str, str, OptionParser) -> None """Given a value spelled "algo:digest", append the digest to a list @@ -843,11 +831,11 @@ def _handle_merge_hash(option, opt_str, value, parser): try: algo, digest = value.split(':', 1) except ValueError: - parser.error('Arguments to {} must be a hash name ' + parser.error('Arguments to {} must be a hash name ' # noqa 'followed by a value, like --hash=sha256:' 'abcde...'.format(opt_str)) if algo not in STRONG_HASHES: - parser.error('Allowed hash algorithms for {} are {}.'.format( + parser.error('Allowed hash algorithms for {} are {}.'.format( # noqa opt_str, ', '.join(STRONG_HASHES))) parser.values.hashes.setdefault(algo, []).append(digest) @@ -915,8 +903,31 @@ unstable_feature = partial( action='append', default=[], choices=['resolver'], - help=SUPPRESS_HELP, # TODO: Enable this when the resolver actually works. - # help='Enable unstable feature(s) that may be backward incompatible.', + help=SUPPRESS_HELP, # TODO: drop this in pip 20.3 +) # type: Callable[..., Option] + +use_new_feature = partial( + Option, + '--use-feature', + dest='features_enabled', + metavar='feature', + action='append', + default=[], + choices=['2020-resolver', 'fast-deps'], + help='Enable new functionality, that may be backward incompatible.', +) # type: Callable[..., Option] + +use_deprecated_feature = partial( + Option, + '--use-deprecated', + dest='deprecated_features_enabled', + metavar='feature', + action='append', + default=[], + choices=[], + help=( + 'Enable deprecated functionality, that will be removed in the future.' + ), ) # type: Callable[..., Option] @@ -948,6 +959,8 @@ general_group = { no_color, no_python_version_warning, unstable_feature, + use_new_feature, + use_deprecated_feature, ] } # type: Dict[str, Any] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py index 7ed2247..6933855 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py @@ -78,6 +78,7 @@ class InterruptibleMixin(object): """ Save the original SIGINT handler for later. """ + # https://github.com/python/mypy/issues/5887 super(InterruptibleMixin, self).__init__( # type: ignore *args, **kwargs @@ -134,6 +135,7 @@ class DownloadProgressMixin(object): def __init__(self, *args, **kwargs): # type: (List[Any], Dict[Any, Any]) -> None + # https://github.com/python/mypy/issues/5887 super(DownloadProgressMixin, self).__init__( # type: ignore *args, **kwargs @@ -165,7 +167,9 @@ class DownloadProgressMixin(object): def iter(self, it): # type: ignore for x in it: yield x - self.next(len(x)) + # B305 is incorrectly raised here + # https://github.com/PyCQA/flake8-bugbear/issues/59 + self.next(len(x)) # noqa: B305 self.finish() @@ -183,6 +187,7 @@ class WindowsMixin(object): if WINDOWS and self.hide_cursor: # type: ignore self.hide_cursor = False + # https://github.com/python/mypy/issues/5887 super(WindowsMixin, self).__init__(*args, **kwargs) # type: ignore # Check if we are running on Windows and we have the colorama module, @@ -206,30 +211,27 @@ class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, message = "%(percent)d%%" suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" -# NOTE: The "type: ignore" comments on the following classes are there to -# work around https://github.com/python/typing/issues/241 - class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar): pass -class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): pass -class DownloadBar(BaseDownloadProgressBar, # type: ignore +class DownloadBar(BaseDownloadProgressBar, Bar): pass -class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore +class DownloadFillingCirclesBar(BaseDownloadProgressBar, FillingCirclesBar): pass -class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar): pass @@ -240,7 +242,8 @@ class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, file = sys.stdout suffix = "%(downloaded)s %(download_speed)s" - def next_phase(self): # type: ignore + def next_phase(self): + # type: () -> str if not hasattr(self, "_phaser"): self._phaser = itertools.cycle(self.phases) return next(self._phaser) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py index 104b033..78b5ce6 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py @@ -13,6 +13,7 @@ from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command from pip._internal.cli.command_context import CommandContextMixIn from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.network.download import Downloader @@ -25,11 +26,7 @@ from pip._internal.req.constructors import ( install_req_from_req_string, ) from pip._internal.req.req_file import parse_requirements -from pip._internal.req.req_set import RequirementSet -from pip._internal.self_outdated_check import ( - make_link_collector, - pip_self_version_check, -) +from pip._internal.self_outdated_check import pip_self_version_check from pip._internal.utils.temp_dir import tempdir_kinds from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -260,7 +257,7 @@ class RequirementCommand(IndexGroupCommand): # The long import name and duplicated invocation is needed to convince # Mypy into correctly typechecking. Otherwise it would complain the # "Resolver" class being redefined. - if 'resolver' in options.unstable_features: + if '2020-resolver' in options.features_enabled: import pip._internal.resolution.resolvelib.resolver return pip._internal.resolution.resolvelib.resolver.Resolver( preparer=preparer, @@ -274,6 +271,7 @@ class RequirementCommand(IndexGroupCommand): force_reinstall=force_reinstall, upgrade_strategy=upgrade_strategy, py_version_info=py_version_info, + lazy_wheel='fast-deps' in options.features_enabled, ) import pip._internal.resolution.legacy.resolver return pip._internal.resolution.legacy.resolver.Resolver( @@ -296,15 +294,12 @@ class RequirementCommand(IndexGroupCommand): options, # type: Values finder, # type: PackageFinder session, # type: PipSession - check_supported_wheels=True, # type: bool ): # type: (...) -> List[InstallRequirement] """ Parse command-line arguments into the corresponding requirements. """ - requirement_set = RequirementSet( - check_supported_wheels=check_supported_wheels - ) + requirements = [] # type: List[InstallRequirement] for filename in options.constraints: for parsed_req in parse_requirements( filename, @@ -313,26 +308,26 @@ class RequirementCommand(IndexGroupCommand): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, + user_supplied=False, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) for req in args: req_to_add = install_req_from_line( req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, + user_supplied=True, isolated=options.isolated_mode, use_pep517=options.use_pep517, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: @@ -342,13 +337,12 @@ class RequirementCommand(IndexGroupCommand): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, - use_pep517=options.use_pep517 + use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) # If any requirement has hash options, enable hash checking. - requirements = requirement_set.all_requirements if any(req.has_hash_options for req in requirements): options.require_hashes = True @@ -392,7 +386,7 @@ class RequirementCommand(IndexGroupCommand): :param ignore_requires_python: Whether to ignore incompatible "Requires-Python" values in links. Defaults to False. """ - link_collector = make_link_collector(session, options=options) + link_collector = LinkCollector.create(session, options=options) selection_prefs = SelectionPreferences( allow_yanked=True, format_control=options.format_control, diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py index ca6d437..747277f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py @@ -24,13 +24,13 @@ class CacheCommand(Command): Subcommands: - dir: Show the cache directory. - info: Show information about the cache. - list: List filenames of packages stored in the cache. - remove: Remove one or more package from the cache. - purge: Remove all items from the cache. + - dir: Show the cache directory. + - info: Show information about the cache. + - list: List filenames of packages stored in the cache. + - remove: Remove one or more package from the cache. + - purge: Remove all items from the cache. - can be a glob expression or a package name. + ```` can be a glob expression or a package name. """ ignore_require_venv = True @@ -59,8 +59,9 @@ class CacheCommand(Command): # Determine action if not args or args[0] not in handlers: - logger.error("Need an action ({}) to perform.".format( - ", ".join(sorted(handlers))) + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), ) return ERROR diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py index 910fcbf..9b99f51 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py @@ -1,13 +1,16 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import sys import textwrap from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS from pip._internal.utils.misc import get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + from optparse import Values BASE_COMPLETION = """ # pip {shell} completion start{script}# pip {shell} completion end @@ -53,33 +56,31 @@ class CompletionCommand(Command): ignore_require_venv = True - def __init__(self, *args, **kw): - super(CompletionCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option( + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( '--bash', '-b', action='store_const', const='bash', dest='shell', help='Emit completion code for bash') - cmd_opts.add_option( + self.cmd_opts.add_option( '--zsh', '-z', action='store_const', const='zsh', dest='shell', help='Emit completion code for zsh') - cmd_opts.add_option( + self.cmd_opts.add_option( '--fish', '-f', action='store_const', const='fish', dest='shell', help='Emit completion code for fish') - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int """Prints the completion code of the given shell""" shells = COMPLETION_SCRIPTS.keys() shell_options = ['--' + shell for shell in sorted(shells)] @@ -89,7 +90,9 @@ class CompletionCommand(Command): prog=get_prog()) ) print(BASE_COMPLETION.format(script=script, shell=options.shell)) + return SUCCESS else: sys.stderr.write( 'ERROR: You must pass {}\n' .format(' or '.join(shell_options)) ) + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py index b801be6..f9b3ab7 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging import os import subprocess @@ -13,21 +10,31 @@ from pip._internal.configuration import ( kinds, ) from pip._internal.exceptions import PipError +from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import get_prog, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Any, Optional + from optparse import Values + + from pip._internal.configuration import Kind logger = logging.getLogger(__name__) class ConfigurationCommand(Command): - """Manage local and global configuration. + """ + Manage local and global configuration. Subcommands: - list: List the active configuration (or from the file specified) - edit: Edit the configuration file in an editor - get: Get the value associated with name - set: Set the name=value - unset: Unset the value associated with name + - list: List the active configuration (or from the file specified) + - edit: Edit the configuration file in an editor + - get: Get the value associated with name + - set: Set the name=value + - unset: Unset the value associated with name + - debug: List the configuration files and values defined under them If none of --user, --global and --site are passed, a virtual environment configuration file is used if one is active and the file @@ -43,13 +50,11 @@ class ConfigurationCommand(Command): %prog [] get name %prog [] set name value %prog [] unset name + %prog [] debug """ - def __init__(self, *args, **kwargs): - super(ConfigurationCommand, self).__init__(*args, **kwargs) - - self.configuration = None - + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '--editor', dest='editor', @@ -88,18 +93,21 @@ class ConfigurationCommand(Command): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int handlers = { "list": self.list_values, "edit": self.open_in_editor, "get": self.get_name, "set": self.set_name_value, - "unset": self.unset_name + "unset": self.unset_name, + "debug": self.list_config_values, } # Determine action if not args or args[0] not in handlers: - logger.error("Need an action ({}) to perform.".format( - ", ".join(sorted(handlers))) + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), ) return ERROR @@ -131,6 +139,7 @@ class ConfigurationCommand(Command): return SUCCESS def _determine_file(self, options, need_value): + # type: (Values, bool) -> Optional[Kind] file_options = [key for key, value in ( (kinds.USER, options.user_file), (kinds.GLOBAL, options.global_file), @@ -157,30 +166,70 @@ class ConfigurationCommand(Command): ) def list_values(self, options, args): + # type: (Values, List[str]) -> None self._get_n_args(args, "list", n=0) for key, value in sorted(self.configuration.items()): write_output("%s=%r", key, value) def get_name(self, options, args): + # type: (Values, List[str]) -> None key = self._get_n_args(args, "get [name]", n=1) value = self.configuration.get_value(key) write_output("%s", value) def set_name_value(self, options, args): + # type: (Values, List[str]) -> None key, value = self._get_n_args(args, "set [name] [value]", n=2) self.configuration.set_value(key, value) self._save_configuration() def unset_name(self, options, args): + # type: (Values, List[str]) -> None key = self._get_n_args(args, "unset [name]", n=1) self.configuration.unset_value(key) self._save_configuration() + def list_config_values(self, options, args): + # type: (Values, List[str]) -> None + """List config key-value pairs across different config files""" + self._get_n_args(args, "debug", n=0) + + self.print_env_var_values() + # Iterate over config files and print if they exist, and the + # key-value pairs present in them if they do + for variant, files in sorted(self.configuration.iter_config_files()): + write_output("%s:", variant) + for fname in files: + with indent_log(): + file_exists = os.path.exists(fname) + write_output("%s, exists: %r", + fname, file_exists) + if file_exists: + self.print_config_file_values(variant) + + def print_config_file_values(self, variant): + # type: (Kind) -> None + """Get key-value pairs from the file of a variant""" + for name, value in self.configuration.\ + get_values_in_config(variant).items(): + with indent_log(): + write_output("%s: %s", name, value) + + def print_env_var_values(self): + # type: () -> None + """Get key-values pairs present as environment variables""" + write_output("%s:", 'env_var') + with indent_log(): + for key, value in sorted(self.configuration.get_environ_vars()): + env_var = 'PIP_{}'.format(key.upper()) + write_output("%s=%r", env_var, value) + def open_in_editor(self, options, args): + # type: (Values, List[str]) -> None editor = self._determine_editor(options) fname = self.configuration.get_file_to_edit() @@ -196,6 +245,7 @@ class ConfigurationCommand(Command): ) def _get_n_args(self, args, example, n): + # type: (List[str], str, int) -> Any """Helper to make sure the command got the right number of arguments """ if len(args) != n: @@ -211,18 +261,19 @@ class ConfigurationCommand(Command): return args def _save_configuration(self): + # type: () -> None # We successfully ran a modifying command. Need to save the # configuration. try: self.configuration.save() except Exception: - logger.error( - "Unable to save configuration. Please report this as a bug.", - exc_info=1 + logger.exception( + "Unable to save configuration. Please report this as a bug." ) raise PipError("Internal Error.") def _determine_editor(self, options): + # type: (Values) -> str if options.editor is not None: return options.editor elif "VISUAL" in os.environ: diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py index b7c92fa..ff369d7 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import locale @@ -23,15 +20,16 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: from types import ModuleType - from typing import Any, List, Optional, Dict + from typing import List, Optional, Dict from optparse import Values + from pip._internal.configuration import Configuration logger = logging.getLogger(__name__) def show_value(name, value): # type: (str, Optional[str]) -> None - logger.info('{}: {}'.format(name, value)) + logger.info('%s: %s', name, value) def show_sys_implementation(): @@ -67,7 +65,6 @@ def create_vendor_txt_map(): def get_module_from_module_name(module_name): # type: (str) -> ModuleType - # Module name can be uppercase in vendor.txt for some reason... module_name = module_name.lower() # PATCH: setuptools is actually only pkg_resources. @@ -84,15 +81,18 @@ def get_module_from_module_name(module_name): def get_vendor_version_from_module(module_name): - # type: (str) -> str - + # type: (str) -> Optional[str] module = get_module_from_module_name(module_name) version = getattr(module, '__version__', None) if not version: # Try to find version in debundled module info + # The type for module.__file__ is Optional[str] in + # Python 2, and str in Python 3. The type: ignore is + # added to account for Python 2, instead of a cast + # and should be removed once we drop Python 2 support pkg_set = pkg_resources.WorkingSet( - [os.path.dirname(getattr(module, '__file__'))] + [os.path.dirname(module.__file__)] # type: ignore ) package = pkg_set.find(pkg_resources.Requirement.parse(module_name)) version = getattr(package, 'version', None) @@ -102,9 +102,9 @@ def get_vendor_version_from_module(module_name): def show_actual_vendor_versions(vendor_txt_versions): # type: (Dict[str, str]) -> None - # Logs the actual version and print extra info - # if there is a conflict or if the actual version could not be imported. - + """Log the actual version and print extra info if there is + a conflict or if the actual version could not be imported. + """ for module_name, expected_version in vendor_txt_versions.items(): extra_message = '' actual_version = get_vendor_version_from_module(module_name) @@ -115,14 +115,7 @@ def show_actual_vendor_versions(vendor_txt_versions): elif actual_version != expected_version: extra_message = ' (CONFLICT: vendor.txt suggests version should'\ ' be {})'.format(expected_version) - - logger.info( - '{name}=={actual}{extra}'.format( - name=module_name, - actual=actual_version, - extra=extra_message - ) - ) + logger.info('%s==%s%s', module_name, actual_version, extra_message) def show_vendor_versions(): @@ -169,8 +162,9 @@ def show_tags(options): def ca_bundle_info(config): + # type: (Configuration) -> str levels = set() - for key, value in config.items(): + for key, _ in config.items(): levels.add(key.split('.')[0]) if not levels: @@ -197,16 +191,14 @@ class DebugCommand(Command): %prog """ ignore_require_venv = True - def __init__(self, *args, **kw): - super(DebugCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - cmdoptions.add_target_python_options(cmd_opts) - self.parser.insert_option_group(0, cmd_opts) + def add_options(self): + # type: () -> None + cmdoptions.add_target_python_options(self.cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) self.parser.config.load() def run(self, options, args): - # type: (Values, List[Any]) -> int + # type: (Values, List[str]) -> int logger.warning( "This command is only meant for debugging. " "Do not use this with automation for parsing and getting these " diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py index c829550..46e8371 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -9,9 +6,15 @@ import os from pip._internal.cli import cmdoptions from pip._internal.cli.cmdoptions import make_target_python from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path, write_output from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List logger = logging.getLogger(__name__) @@ -36,28 +39,25 @@ class DownloadCommand(RequirementCommand): %prog [options] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(DownloadCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) - cmd_opts = self.cmd_opts - - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.build_dir()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.global_options()) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option(cmdoptions.pre()) - cmd_opts.add_option(cmdoptions.require_hashes()) - cmd_opts.add_option(cmdoptions.progress_bar()) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) - - cmd_opts.add_option( + self.cmd_opts.add_option( '-d', '--dest', '--destination-dir', '--destination-directory', dest='download_dir', metavar='dir', @@ -65,7 +65,7 @@ class DownloadCommand(RequirementCommand): help=("Download packages into ."), ) - cmdoptions.add_target_python_options(cmd_opts) + cmdoptions.add_target_python_options(self.cmd_opts) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -73,10 +73,12 @@ class DownloadCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup def run(self, options, args): + # type: (Values, List[str]) -> int + options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. @@ -132,11 +134,10 @@ class DownloadCommand(RequirementCommand): reqs, check_supported_wheels=True ) - downloaded = ' '.join([ - req.name for req in requirement_set.requirements.values() - if req.successfully_downloaded - ]) + downloaded = ' '.join([req.name # type: ignore + for req in requirement_set.requirements.values() + if req.successfully_downloaded]) if downloaded: write_output('Successfully downloaded %s', downloaded) - return requirement_set + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py index 1317177..2071fba 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import sys @@ -8,12 +5,18 @@ import sys from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS from pip._internal.models.format_control import FormatControl from pip._internal.operations.freeze import freeze from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.typing import MYPY_CHECK_RUNNING DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + class FreezeCommand(Command): """ @@ -26,9 +29,8 @@ class FreezeCommand(Command): %prog [options]""" log_streams = ("ext://sys.stderr", "ext://sys.stderr") - def __init__(self, *args, **kw): - super(FreezeCommand, self).__init__(*args, **kw) - + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '-r', '--requirement', dest='requirements', @@ -75,6 +77,7 @@ class FreezeCommand(Command): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int format_control = FormatControl(set(), set()) wheel_cache = WheelCache(options.cache_dir, format_control) skip = set(stdlib_pkgs) @@ -97,3 +100,4 @@ class FreezeCommand(Command): for line in freeze(**freeze_kwargs): sys.stdout.write(line + '\n') + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py index f266861..37831c3 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import hashlib @@ -8,9 +5,14 @@ import logging import sys from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES from pip._internal.utils.misc import read_chunks, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List logger = logging.getLogger(__name__) @@ -26,8 +28,8 @@ class HashCommand(Command): usage = '%prog [options] ...' ignore_require_venv = True - def __init__(self, *args, **kw): - super(HashCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '-a', '--algorithm', dest='algorithm', @@ -39,6 +41,7 @@ class HashCommand(Command): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int if not args: self.parser.print_usage(sys.stderr) return ERROR @@ -47,9 +50,11 @@ class HashCommand(Command): for path in args: write_output('%s:\n--hash=%s:%s', path, algorithm, _hash_of_file(path, algorithm)) + return SUCCESS def _hash_of_file(path, algorithm): + # type: (str, str) -> str """Return the hash digest of a file.""" with open(path, 'rb') as archive: hash = hashlib.new(algorithm) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py index c17d7a4..a2edc29 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py @@ -1,11 +1,13 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + from optparse import Values class HelpCommand(Command): @@ -16,6 +18,7 @@ class HelpCommand(Command): ignore_require_venv = True def run(self, options, args): + # type: (Values, List[str]) -> int from pip._internal.commands import ( commands_dict, create_command, get_similar_commands, ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py index 70bda2e..8c2c32f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py @@ -1,10 +1,3 @@ -# The following comment should be removed at some point in the future. -# It's included for now because without it InstallCommand.run() has a -# couple errors where we have to know req.name is str rather than -# Optional[str] for the InstallRequirement req. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import errno @@ -28,12 +21,14 @@ from pip._internal.locations import distutils_scheme from pip._internal.operations.check import check_install_conflicts from pip._internal.req import install_given_reqs from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.datetime import today_is_later_than from pip._internal.utils.deprecation import deprecated from pip._internal.utils.distutils_args import parse_distutils_args from pip._internal.utils.filesystem import test_writable_dir from pip._internal.utils.misc import ( ensure_dir, get_installed_version, + get_pip_version, protect_pip_from_modification_on_windows, write_output, ) @@ -44,9 +39,10 @@ from pip._internal.wheel_builder import build, should_build_for_install_command if MYPY_CHECK_RUNNING: from optparse import Values - from typing import Any, Iterable, List, Optional + from typing import Iterable, List, Optional from pip._internal.models.format_control import FormatControl + from pip._internal.operations.check import ConflictDetails from pip._internal.req.req_install import InstallRequirement from pip._internal.wheel_builder import BinaryAllowedPredicate @@ -87,18 +83,15 @@ class InstallCommand(RequirementCommand): %prog [options] [-e] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(InstallCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.pre()) - cmd_opts = self.cmd_opts - - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.pre()) - - cmd_opts.add_option(cmdoptions.editable()) - cmd_opts.add_option( + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option( '-t', '--target', dest='target_dir', metavar='dir', @@ -108,9 +101,9 @@ class InstallCommand(RequirementCommand): '. Use --upgrade to replace existing packages in ' 'with new versions.' ) - cmdoptions.add_target_python_options(cmd_opts) + cmdoptions.add_target_python_options(self.cmd_opts) - cmd_opts.add_option( + self.cmd_opts.add_option( '--user', dest='use_user_site', action='store_true', @@ -118,19 +111,19 @@ class InstallCommand(RequirementCommand): "platform. Typically ~/.local/, or %APPDATA%\\Python on " "Windows. (See the Python documentation for site.USER_BASE " "for full details.)") - cmd_opts.add_option( + self.cmd_opts.add_option( '--no-user', dest='use_user_site', action='store_false', help=SUPPRESS_HELP) - cmd_opts.add_option( + self.cmd_opts.add_option( '--root', dest='root_path', metavar='dir', default=None, help="Install everything relative to this alternate root " "directory.") - cmd_opts.add_option( + self.cmd_opts.add_option( '--prefix', dest='prefix_path', metavar='dir', @@ -138,11 +131,11 @@ class InstallCommand(RequirementCommand): help="Installation prefix where lib, bin and other top-level " "folders are placed") - cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.build_dir()) - cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option( + self.cmd_opts.add_option( '-U', '--upgrade', dest='upgrade', action='store_true', @@ -151,7 +144,7 @@ class InstallCommand(RequirementCommand): 'upgrade-strategy used.' ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--upgrade-strategy', dest='upgrade_strategy', default='only-if-needed', @@ -165,14 +158,14 @@ class InstallCommand(RequirementCommand): 'satisfy the requirements of the upgraded package(s).' ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--force-reinstall', dest='force_reinstall', action='store_true', help='Reinstall all packages even if they are already ' 'up-to-date.') - cmd_opts.add_option( + self.cmd_opts.add_option( '-I', '--ignore-installed', dest='ignore_installed', action='store_true', @@ -182,15 +175,15 @@ class InstallCommand(RequirementCommand): 'with a different package manager!' ) - cmd_opts.add_option(cmdoptions.ignore_requires_python()) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) - cmd_opts.add_option(cmdoptions.install_options()) - cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.install_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) - cmd_opts.add_option( + self.cmd_opts.add_option( "--compile", action="store_true", dest="compile", @@ -198,21 +191,21 @@ class InstallCommand(RequirementCommand): help="Compile Python source files to bytecode", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-compile", action="store_false", dest="compile", help="Do not compile Python source files to bytecode", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-warn-script-location", action="store_false", dest="warn_script_location", default=True, help="Do not warn when installing scripts outside PATH", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-warn-conflicts", action="store_false", dest="warn_about_conflicts", @@ -220,11 +213,11 @@ class InstallCommand(RequirementCommand): help="Do not warn about broken dependencies", ) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option(cmdoptions.require_hashes()) - cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -232,11 +225,11 @@ class InstallCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup def run(self, options, args): - # type: (Values, List[Any]) -> int + # type: (Values, List[str]) -> int if options.use_user_site and options.target_dir is not None: raise CommandError("Can not combine '--user' and '--target'") @@ -249,6 +242,7 @@ class InstallCommand(RequirementCommand): install_options = options.install_options or [] + logger.debug("Using %s", get_pip_version()) options.use_user_site = decide_user_install( options.use_user_site, prefix_path=options.prefix_path, @@ -272,6 +266,7 @@ class InstallCommand(RequirementCommand): # Create a target directory for using with the target option target_temp_dir = TempDirectory(kind="target") target_temp_dir_path = target_temp_dir.path + self.enter_context(target_temp_dir) global_options = options.global_options or [] @@ -297,12 +292,9 @@ class InstallCommand(RequirementCommand): ) try: - reqs = self.get_requirements( - args, options, finder, session, - check_supported_wheels=not options.target_dir, - ) + reqs = self.get_requirements(args, options, finder, session) - warn_deprecated_install_options( + reject_location_related_install_options( reqs, options.install_options ) @@ -336,7 +328,7 @@ class InstallCommand(RequirementCommand): try: pip_req = requirement_set.get_requirement("pip") except KeyError: - modifying_pip = None + modifying_pip = False else: # If we're not replacing an already installed pip, # we're not modifying it. @@ -365,29 +357,51 @@ class InstallCommand(RequirementCommand): # If we're using PEP 517, we cannot do a direct install # so we fail here. - # We don't care about failures building legacy - # requirements, as we'll fall through to a direct - # install for those. - pep517_build_failures = [ - r for r in build_failures if r.use_pep517 - ] - if pep517_build_failures: + pep517_build_failure_names = [ + r.name # type: ignore + for r in build_failures if r.use_pep517 + ] # type: List[str] + if pep517_build_failure_names: raise InstallationError( "Could not build wheels for {} which use" " PEP 517 and cannot be installed directly".format( - ", ".join(r.name for r in pep517_build_failures))) + ", ".join(pep517_build_failure_names) + ) + ) + + # For now, we just warn about failures building legacy + # requirements, as we'll fall through to a direct + # install for those. + legacy_build_failure_names = [ + r.name # type: ignore + for r in build_failures if not r.use_pep517 + ] # type: List[str] + if legacy_build_failure_names: + deprecated( + reason=( + "Could not build wheels for {} which do not use " + "PEP 517. pip will fall back to legacy 'setup.py " + "install' for these.".format( + ", ".join(legacy_build_failure_names) + ) + ), + replacement="to fix the wheel build issue reported above", + gone_in="21.0", + issue=8368, + ) to_install = resolver.get_installation_order( requirement_set ) - # Consistency Checking of the package set we're installing. + # Check for conflicts in the package set we're installing. + conflicts = None # type: Optional[ConflictDetails] should_warn_about_conflicts = ( not options.ignore_dependencies and options.warn_about_conflicts ) if should_warn_about_conflicts: - self._warn_about_conflicts(to_install) + conflicts = self._determine_conflicts(to_install) # Don't warn about script install locations if # --target has been specified @@ -402,9 +416,9 @@ class InstallCommand(RequirementCommand): root=options.root_path, home=target_temp_dir_path, prefix=options.prefix_path, - pycompile=options.compile, warn_script_location=warn_script_location, use_user_site=options.use_user_site, + pycompile=options.compile, ) lib_locations = get_lib_location_guesses( @@ -429,6 +443,13 @@ class InstallCommand(RequirementCommand): except Exception: pass items.append(item) + + if conflicts is not None: + self._warn_about_conflicts( + conflicts, + new_resolver='2020-resolver' in options.features_enabled, + ) + installed_desc = ' '.join(items) if installed_desc: write_output( @@ -440,11 +461,12 @@ class InstallCommand(RequirementCommand): message = create_env_error_message( error, show_traceback, options.use_user_site, ) - logger.error(message, exc_info=show_traceback) + logger.error(message, exc_info=show_traceback) # noqa return ERROR if options.target_dir: + assert target_temp_dir self._handle_target_dir( options.target_dir, target_temp_dir, options.upgrade ) @@ -452,96 +474,154 @@ class InstallCommand(RequirementCommand): return SUCCESS def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + # type: (str, TempDirectory, bool) -> None ensure_dir(target_dir) # Checking both purelib and platlib directories for installed # packages to be moved to target directory lib_dir_list = [] - with target_temp_dir: - # Checking both purelib and platlib directories for installed - # packages to be moved to target directory - scheme = distutils_scheme('', home=target_temp_dir.path) - purelib_dir = scheme['purelib'] - platlib_dir = scheme['platlib'] - data_dir = scheme['data'] + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] - if os.path.exists(purelib_dir): - lib_dir_list.append(purelib_dir) - if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: - lib_dir_list.append(platlib_dir) - if os.path.exists(data_dir): - lib_dir_list.append(data_dir) + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) - for lib_dir in lib_dir_list: - for item in os.listdir(lib_dir): - if lib_dir == data_dir: - ddir = os.path.join(data_dir, item) - if any(s.startswith(ddir) for s in lib_dir_list[:-1]): - continue - target_item_dir = os.path.join(target_dir, item) - if os.path.exists(target_item_dir): - if not upgrade: - logger.warning( - 'Target directory %s already exists. Specify ' - '--upgrade to force replacement.', - target_item_dir - ) - continue - if os.path.islink(target_item_dir): - logger.warning( - 'Target directory %s already exists and is ' - 'a link. pip will not automatically replace ' - 'links, please remove if replacement is ' - 'desired.', - target_item_dir - ) - continue - if os.path.isdir(target_item_dir): - shutil.rmtree(target_item_dir) - else: - os.remove(target_item_dir) + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) - shutil.move( - os.path.join(lib_dir, item), - target_item_dir - ) + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) - def _warn_about_conflicts(self, to_install): + def _determine_conflicts(self, to_install): + # type: (List[InstallRequirement]) -> Optional[ConflictDetails] try: - package_set, _dep_info = check_install_conflicts(to_install) + return check_install_conflicts(to_install) except Exception: - logger.error("Error checking for conflicts.", exc_info=True) - return - missing, conflicting = _dep_info + logger.exception( + "Error while checking for conflicts. Please file an issue on " + "pip's issue tracker: https://github.com/pypa/pip/issues/new" + ) + return None - # NOTE: There is some duplication here from pip check + def _warn_about_conflicts(self, conflict_details, new_resolver): + # type: (ConflictDetails, bool) -> None + package_set, (missing, conflicting) = conflict_details + if not missing and not conflicting: + return + + parts = [] # type: List[str] + if not new_resolver: + parts.append( + "After October 2020 you may experience errors when installing " + "or updating packages. This is because pip will change the " + "way that it resolves dependency conflicts.\n" + ) + parts.append( + "We recommend you use --use-feature=2020-resolver to test " + "your packages with the new resolver before it becomes the " + "default.\n" + ) + elif not today_is_later_than(year=2020, month=7, day=31): + # NOTE: trailing newlines here are intentional + parts.append( + "Pip will install or upgrade your package(s) and its " + "dependencies without taking into account other packages you " + "already have installed. This may cause an uncaught " + "dependency conflict.\n" + ) + form_link = "https://forms.gle/cWKMoDs8sUVE29hz9" + parts.append( + "If you would like pip to take your other packages into " + "account, please tell us here: {}\n".format(form_link) + ) + + # NOTE: There is some duplication here, with commands/check.py for project_name in missing: version = package_set[project_name][0] for dependency in missing[project_name]: - logger.critical( - "%s %s requires %s, which is not installed.", - project_name, version, dependency[1], + message = ( + "{name} {version} requires {requirement}, " + "which is not installed." + ).format( + name=project_name, + version=version, + requirement=dependency[1], ) + parts.append(message) for project_name in conflicting: version = package_set[project_name][0] for dep_name, dep_version, req in conflicting[project_name]: - logger.critical( - "%s %s has requirement %s, but you'll have %s %s which is " - "incompatible.", - project_name, version, req, dep_name, dep_version, + message = ( + "{name} {version} requires {requirement}, but you'll have " + "{dep_name} {dep_version} which is incompatible." + ).format( + name=project_name, + version=version, + requirement=req, + dep_name=dep_name, + dep_version=dep_version, ) + parts.append(message) + + logger.critical("\n".join(parts)) -def get_lib_location_guesses(*args, **kwargs): - scheme = distutils_scheme('', *args, **kwargs) +def get_lib_location_guesses( + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None # type: Optional[str] +): + # type:(...) -> List[str] + scheme = distutils_scheme('', user=user, home=home, root=root, + isolated=isolated, prefix=prefix) return [scheme['purelib'], scheme['platlib']] -def site_packages_writable(**kwargs): +def site_packages_writable(root, isolated): + # type: (Optional[str], bool) -> bool return all( - test_writable_dir(d) for d in set(get_lib_location_guesses(**kwargs)) + test_writable_dir(d) for d in set( + get_lib_location_guesses(root=root, isolated=isolated)) ) @@ -605,7 +685,7 @@ def decide_user_install( return True -def warn_deprecated_install_options(requirements, options): +def reject_location_related_install_options(requirements, options): # type: (List[InstallRequirement], Optional[List[str]]) -> None """If any location-changing --install-option arguments were passed for requirements or on the command-line, then show a deprecation warning. @@ -638,24 +718,17 @@ def warn_deprecated_install_options(requirements, options): if not offenders: return - deprecated( - reason=( - "Location-changing options found in --install-option: {}. " - "This configuration may cause unexpected behavior and is " - "unsupported.".format( - "; ".join(offenders) - ) - ), - replacement=( - "using pip-level options like --user, --prefix, --root, and " - "--target" - ), - gone_in="20.2", - issue=7309, + raise CommandError( + "Location-changing options found in --install-option: {}." + " This is unsupported, use pip-level options like --user," + " --prefix, --root, and --target instead.".format( + "; ".join(offenders) + ) ) def create_env_error_message(error, show_traceback, using_user_site): + # type: (EnvironmentError, bool, bool) -> str """Format an error message for an EnvironmentError It may occur anytime during the execution of the install command. diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py index 2aa3075..20e9bff 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import json @@ -10,10 +7,11 @@ from pip._vendor import six from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError +from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.models.selection_prefs import SelectionPreferences -from pip._internal.self_outdated_check import make_link_collector from pip._internal.utils.misc import ( dist_is_editable, get_installed_distributions, @@ -21,6 +19,15 @@ from pip._internal.utils.misc import ( write_output, ) from pip._internal.utils.packaging import get_installer +from pip._internal.utils.parallel import map_multithread +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Set, Tuple, Iterator + + from pip._internal.network.session import PipSession + from pip._vendor.pkg_resources import Distribution logger = logging.getLogger(__name__) @@ -32,30 +39,28 @@ class ListCommand(IndexGroupCommand): Packages are listed in a case-insensitive sorted order. """ + ignore_require_venv = True usage = """ %prog [options]""" - def __init__(self, *args, **kw): - super(ListCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option( + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( '-o', '--outdated', action='store_true', default=False, help='List outdated packages') - cmd_opts.add_option( + self.cmd_opts.add_option( '-u', '--uptodate', action='store_true', default=False, help='List uptodate packages') - cmd_opts.add_option( + self.cmd_opts.add_option( '-e', '--editable', action='store_true', default=False, help='List editable projects.') - cmd_opts.add_option( + self.cmd_opts.add_option( '-l', '--local', action='store_true', default=False, @@ -68,8 +73,8 @@ class ListCommand(IndexGroupCommand): action='store_true', default=False, help='Only output packages installed in user-site.') - cmd_opts.add_option(cmdoptions.list_path()) - cmd_opts.add_option( + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( '--pre', action='store_true', default=False, @@ -77,7 +82,7 @@ class ListCommand(IndexGroupCommand): "pip only finds stable versions."), ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--format', action='store', dest='list_format', @@ -87,7 +92,7 @@ class ListCommand(IndexGroupCommand): "or json", ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--not-required', action='store_true', dest='not_required', @@ -95,13 +100,13 @@ class ListCommand(IndexGroupCommand): "installed packages.", ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--exclude-editable', action='store_false', dest='include_editable', help='Exclude editable package from output.', ) - cmd_opts.add_option( + self.cmd_opts.add_option( '--include-editable', action='store_true', dest='include_editable', @@ -113,13 +118,14 @@ class ListCommand(IndexGroupCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) def _build_package_finder(self, options, session): + # type: (Values, PipSession) -> PackageFinder """ Create a package finder appropriate to this list command. """ - link_collector = make_link_collector(session, options=options) + link_collector = LinkCollector.create(session, options=options) # Pass allow_yanked=False to ignore yanked versions. selection_prefs = SelectionPreferences( @@ -133,6 +139,7 @@ class ListCommand(IndexGroupCommand): ) def run(self, options, args): + # type: (Values, List[str]) -> int if options.outdated and options.uptodate: raise CommandError( "Options --outdated and --uptodate cannot be combined.") @@ -160,30 +167,40 @@ class ListCommand(IndexGroupCommand): packages = self.get_uptodate(packages, options) self.output_package_listing(packages, options) + return SUCCESS def get_outdated(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] return [ dist for dist in self.iter_packages_latest_infos(packages, options) if dist.latest_version > dist.parsed_version ] def get_uptodate(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] return [ dist for dist in self.iter_packages_latest_infos(packages, options) if dist.latest_version == dist.parsed_version ] def get_not_required(self, packages, options): - dep_keys = set() + # type: (List[Distribution], Values) -> List[Distribution] + dep_keys = set() # type: Set[Distribution] for dist in packages: dep_keys.update(requirement.key for requirement in dist.requires()) - return {pkg for pkg in packages if pkg.key not in dep_keys} + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.key not in dep_keys}) def iter_packages_latest_infos(self, packages, options): + # type: (List[Distribution], Values) -> Iterator[Distribution] with self._build_session(options) as session: finder = self._build_package_finder(options, session) def latest_info(dist): + # type: (Distribution) -> Distribution typ = 'unknown' all_candidates = finder.find_all_candidates(dist.key) if not options.pre: @@ -208,11 +225,12 @@ class ListCommand(IndexGroupCommand): dist.latest_filetype = typ return dist - for dist in map(latest_info, packages): + for dist in map_multithread(latest_info, packages): if dist is not None: yield dist def output_package_listing(self, packages, options): + # type: (List[Distribution], Values) -> None packages = sorted( packages, key=lambda dist: dist.project_name.lower(), @@ -231,6 +249,7 @@ class ListCommand(IndexGroupCommand): write_output(format_for_json(packages, options)) def output_package_listing_columns(self, data, header): + # type: (List[List[str]], List[str]) -> None # insert the header first: we need to know the size of column names if len(data) > 0: data.insert(0, header) @@ -246,6 +265,7 @@ class ListCommand(IndexGroupCommand): def format_for_columns(pkgs, options): + # type: (List[Distribution], Values) -> Tuple[List[List[str]], List[str]] """ Convert the package data into something usable by output_package_listing_columns. @@ -283,6 +303,7 @@ def format_for_columns(pkgs, options): def format_for_json(packages, options): + # type: (List[Distribution], Values) -> str data = [] for dist in packages: info = { diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py index e5f286e..ff09472 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -22,7 +19,17 @@ from pip._internal.models.index import PyPI from pip._internal.network.xmlrpc import PipXmlrpcTransport from pip._internal.utils.compat import get_terminal_size from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import write_output +from pip._internal.utils.misc import get_distribution, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Dict, Optional + from typing_extensions import TypedDict + TransformedHit = TypedDict( + 'TransformedHit', + {'name': str, 'summary': str, 'versions': List[str]}, + ) logger = logging.getLogger(__name__) @@ -34,8 +41,8 @@ class SearchCommand(Command, SessionCommandMixin): %prog [options] """ ignore_require_venv = True - def __init__(self, *args, **kw): - super(SearchCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '-i', '--index', dest='index', @@ -46,6 +53,7 @@ class SearchCommand(Command, SessionCommandMixin): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int if not args: raise CommandError('Missing required argument (search query).') query = args @@ -62,6 +70,7 @@ class SearchCommand(Command, SessionCommandMixin): return NO_MATCHES_FOUND def search(self, query, options): + # type: (List[str], Values) -> List[Dict[str, str]] index_url = options.index session = self.get_default_session(options) @@ -73,12 +82,13 @@ class SearchCommand(Command, SessionCommandMixin): def transform_hits(hits): + # type: (List[Dict[str, str]]) -> List[TransformedHit] """ The list from pypi is really a list of versions. We want a list of packages with the list of versions stored inline. This converts the list from pypi into one we can use. """ - packages = OrderedDict() + packages = OrderedDict() # type: OrderedDict[str, TransformedHit] for hit in hits: name = hit['name'] summary = hit['summary'] @@ -101,6 +111,7 @@ def transform_hits(hits): def print_results(hits, name_column_width=None, terminal_width=None): + # type: (List[TransformedHit], Optional[int], Optional[int]) -> None if not hits: return if name_column_width is None: @@ -118,8 +129,9 @@ def print_results(hits, name_column_width=None, terminal_width=None): target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal - summary = textwrap.wrap(summary, target_width) - summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + summary_lines = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join( + summary_lines) line = '{name_latest:{name_column_width}} - {summary}'.format( name_latest='{name} ({latest})'.format(**locals()), @@ -127,7 +139,8 @@ def print_results(hits, name_column_width=None, terminal_width=None): try: write_output(line) if name in installed_packages: - dist = pkg_resources.get_distribution(name) + dist = get_distribution(name) + assert dist is not None with indent_log(): if dist.version == latest: write_output('INSTALLED: %s (latest)', dist.version) @@ -143,4 +156,5 @@ def print_results(hits, name_column_width=None, terminal_width=None): def highest_version(versions): + # type: (List[str]) -> str return max(versions, key=parse_version) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py index a61294b..3892c59 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -13,6 +10,11 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Dict, Iterator logger = logging.getLogger(__name__) @@ -28,8 +30,8 @@ class ShowCommand(Command): %prog [options] ...""" ignore_require_venv = True - def __init__(self, *args, **kw): - super(ShowCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '-f', '--files', dest='files', @@ -40,6 +42,7 @@ class ShowCommand(Command): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int if not args: logger.warning('ERROR: Please provide a package name or names.') return ERROR @@ -53,6 +56,7 @@ class ShowCommand(Command): def search_packages_info(query): + # type: (List[str]) -> Iterator[Dict[str, str]] """ Gather details from installed distributions. Print distribution name, version, location, and installed files. Installed files requires a @@ -71,6 +75,7 @@ def search_packages_info(query): logger.warning('Package(s) not found: %s', ', '.join(missing)) def get_requiring_packages(package_name): + # type: (str) -> List[str] canonical_name = canonicalize_name(package_name) return [ pkg.project_name for pkg in pkg_resources.working_set @@ -88,12 +93,12 @@ def search_packages_info(query): 'required_by': get_requiring_packages(dist.project_name) } file_list = None - metadata = None + metadata = '' if isinstance(dist, pkg_resources.DistInfoDistribution): # RECORDs should be part of .dist-info metadatas if dist.has_metadata('RECORD'): lines = dist.get_metadata_lines('RECORD') - paths = [l.split(',')[0] for l in lines] + paths = [line.split(',')[0] for line in lines] paths = [os.path.join(dist.location, p) for p in paths] file_list = [os.path.relpath(p, dist.location) for p in paths] @@ -141,6 +146,7 @@ def search_packages_info(query): def print_results(distributions, list_files=False, verbose=False): + # type: (Iterator[Dict[str, str]], bool, bool) -> bool """ Print the information from installed distributions found. """ diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py index 5db4fb4..3371fe4 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py @@ -1,12 +1,10 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import InstallationError from pip._internal.req import parse_requirements from pip._internal.req.constructors import ( @@ -14,6 +12,11 @@ from pip._internal.req.constructors import ( install_req_from_parsed_requirement, ) from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List class UninstallCommand(Command, SessionCommandMixin): @@ -31,8 +34,8 @@ class UninstallCommand(Command, SessionCommandMixin): %prog [options] ... %prog [options] -r ...""" - def __init__(self, *args, **kw): - super(UninstallCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None self.cmd_opts.add_option( '-r', '--requirement', dest='requirements', @@ -51,6 +54,7 @@ class UninstallCommand(Command, SessionCommandMixin): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # type: (Values, List[str]) -> int session = self.get_default_session(options) reqs_to_uninstall = {} @@ -87,3 +91,5 @@ class UninstallCommand(Command, SessionCommandMixin): ) if uninstall_pathset: uninstall_pathset.commit() + + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py index 48f3bfa..0f71856 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -12,6 +9,7 @@ import shutil from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path @@ -21,7 +19,7 @@ from pip._internal.wheel_builder import build, should_build_for_wheel_command if MYPY_CHECK_RUNNING: from optparse import Values - from typing import Any, List + from typing import List logger = logging.getLogger(__name__) @@ -49,12 +47,10 @@ class WheelCommand(RequirementCommand): %prog [options] [-e] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(WheelCommand, self).__init__(*args, **kw) + def add_options(self): + # type: () -> None - cmd_opts = self.cmd_opts - - cmd_opts.add_option( + self.cmd_opts.add_option( '-w', '--wheel-dir', dest='wheel_dir', metavar='dir', @@ -62,29 +58,29 @@ class WheelCommand(RequirementCommand): help=("Build wheels into , where the default is the " "current working directory."), ) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option( + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option( '--build-option', dest='build_options', metavar='options', action='append', help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", ) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.editable()) - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option(cmdoptions.ignore_requires_python()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.build_dir()) - cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) - cmd_opts.add_option( + self.cmd_opts.add_option( '--global-option', dest='global_options', action='append', @@ -92,7 +88,7 @@ class WheelCommand(RequirementCommand): help="Extra global options to be supplied to the setup.py " "call before the 'bdist_wheel' command.") - cmd_opts.add_option( + self.cmd_opts.add_option( '--pre', action='store_true', default=False, @@ -100,7 +96,7 @@ class WheelCommand(RequirementCommand): "pip only finds stable versions."), ) - cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -108,11 +104,11 @@ class WheelCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup def run(self, options, args): - # type: (Values, List[Any]) -> None + # type: (Values, List[str]) -> int cmdoptions.check_install_build_global(options) session = self.get_default_session(options) @@ -188,3 +184,5 @@ class WheelCommand(RequirementCommand): raise CommandError( "Failed to build one or more wheels" ) + + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/configuration.py b/venv/lib/python3.8/site-packages/pip/_internal/configuration.py index 2648b8a..e49a5f4 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/configuration.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/configuration.py @@ -11,9 +11,6 @@ Some terminology: A single word describing where the configuration key-value pair came from """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import locale import logging import os @@ -114,7 +111,7 @@ class Configuration(object): """ def __init__(self, isolated, load_only=None): - # type: (bool, Kind) -> None + # type: (bool, Optional[Kind]) -> None super(Configuration, self).__init__() _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.SITE, None] @@ -124,8 +121,8 @@ class Configuration(object): ", ".join(map(repr, _valid_load_only[:-1])) ) ) - self.isolated = isolated # type: bool - self.load_only = load_only # type: Optional[Kind] + self.isolated = isolated + self.load_only = load_only # The order here determines the override order. self._override_order = [ @@ -185,6 +182,7 @@ class Configuration(object): """ self._ensure_have_load_only() + assert self.load_only fname, parser = self._get_parser_to_modify() if parser is not None: @@ -200,10 +198,10 @@ class Configuration(object): def unset_value(self, key): # type: (str) -> None - """Unset a value in the configuration. - """ + """Unset a value in the configuration.""" self._ensure_have_load_only() + assert self.load_only if key not in self._config[self.load_only]: raise ConfigurationError("No such key - {}".format(key)) @@ -211,30 +209,18 @@ class Configuration(object): if parser is not None: section, name = _disassemble_key(key) - - # Remove the key in the parser - modified_something = False - if parser.has_section(section): - # Returns whether the option was removed or not - modified_something = parser.remove_option(section, name) - - if modified_something: - # name removed from parser, section may now be empty - section_iter = iter(parser.items(section)) - try: - val = next(section_iter) - except StopIteration: - val = None - - if val is None: - parser.remove_section(section) - - self._mark_as_modified(fname, parser) - else: + if not (parser.has_section(section) + and parser.remove_option(section, name)): + # The option was not removed. raise ConfigurationError( "Fatal Internal error [id=1]. Please report as a bug." ) + # The section may be empty after the option was removed. + if not parser.items(section): + parser.remove_section(section) + self._mark_as_modified(fname, parser) + del self._config[self.load_only][key] def save(self): @@ -280,7 +266,7 @@ class Configuration(object): # type: () -> None """Loads configuration from configuration files """ - config_files = dict(self._iter_config_files()) + config_files = dict(self.iter_config_files()) if config_files[kinds.ENV][0:1] == [os.devnull]: logger.debug( "Skipping loading configuration files due to " @@ -342,7 +328,7 @@ class Configuration(object): """Loads configuration from environment variables """ self._config[kinds.ENV_VAR].update( - self._normalized_keys(":env:", self._get_environ_vars()) + self._normalized_keys(":env:", self.get_environ_vars()) ) def _normalized_keys(self, section, items): @@ -358,7 +344,7 @@ class Configuration(object): normalized[key] = val return normalized - def _get_environ_vars(self): + def get_environ_vars(self): # type: () -> Iterable[Tuple[str, str]] """Returns a generator with all environmental vars with prefix PIP_""" for key, val in os.environ.items(): @@ -370,7 +356,7 @@ class Configuration(object): yield key[4:].lower(), val # XXX: This is patched in the tests. - def _iter_config_files(self): + def iter_config_files(self): # type: () -> Iterable[Tuple[Kind, List[str]]] """Yields variant and configuration files associated with it. @@ -401,9 +387,15 @@ class Configuration(object): # finally virtualenv configuration first trumping others yield kinds.SITE, config_files[kinds.SITE] + def get_values_in_config(self, variant): + # type: (Kind) -> Dict[str, Any] + """Get values present in a config file""" + return self._config[variant] + def _get_parser_to_modify(self): # type: () -> Tuple[str, RawConfigParser] # Determine which parser to modify + assert self.load_only parsers = self._parsers[self.load_only] if not parsers: # This should not happen if everything works correctly. diff --git a/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py b/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py index 8ac8548..3f26215 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py @@ -1,8 +1,5 @@ """Exceptions used throughout package""" -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import from itertools import chain, groupby, repeat @@ -12,10 +9,20 @@ from pip._vendor.six import iteritems from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Optional + from typing import Any, Optional, List, Dict, Text + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Response, Request + from pip._vendor.six import PY3 + from pip._vendor.six.moves import configparser + from pip._internal.req.req_install import InstallRequirement + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + class PipError(Exception): """Base pip exception""" @@ -84,10 +91,38 @@ class CommandError(PipError): """Raised when there is an error in command-line arguments""" +class SubProcessError(PipError): + """Raised when there is an error raised while executing a + command in subprocess""" + + class PreviousBuildDirError(PipError): """Raised when there's a previous conflicting build directory""" +class NetworkConnectionError(PipError): + """HTTP connection error""" + + def __init__(self, error_msg, response=None, request=None): + # type: (Text, Response, Request) -> None + """ + Initialize NetworkConnectionError with `request` and `response` + objects. + """ + self.response = response + self.request = request + self.error_msg = error_msg + if (self.response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(NetworkConnectionError, self).__init__( + error_msg, response, request) + + def __str__(self): + # type: () -> str + return str(self.error_msg) + + class InvalidWheelFilename(InstallationError): """Invalid wheel filename.""" @@ -96,16 +131,39 @@ class UnsupportedWheel(InstallationError): """Unsupported wheel.""" +class MetadataInconsistent(InstallationError): + """Built metadata contains inconsistent information. + + This is raised when the metadata contains values (e.g. name and version) + that do not match the information previously obtained from sdist filename + or user-supplied ``#egg=`` value. + """ + def __init__(self, ireq, field, built): + # type: (InstallRequirement, str, Any) -> None + self.ireq = ireq + self.field = field + self.built = built + + def __str__(self): + # type: () -> str + return "Requested {} has different {} in metadata: {!r}".format( + self.ireq, self.field, self.built, + ) + + class HashErrors(InstallationError): """Multiple HashError instances rolled into one for reporting""" def __init__(self): - self.errors = [] + # type: () -> None + self.errors = [] # type: List[HashError] def append(self, error): + # type: (HashError) -> None self.errors.append(error) def __str__(self): + # type: () -> str lines = [] self.errors.sort(key=lambda e: e.order) for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): @@ -113,11 +171,14 @@ class HashErrors(InstallationError): lines.extend(e.body() for e in errors_of_cls) if lines: return '\n'.join(lines) + return '' def __nonzero__(self): + # type: () -> bool return bool(self.errors) def __bool__(self): + # type: () -> bool return self.__nonzero__() @@ -139,8 +200,10 @@ class HashError(InstallationError): """ req = None # type: Optional[InstallRequirement] head = '' + order = None # type: Optional[int] def body(self): + # type: () -> str """Return a summary of me for display under the heading. This default implementation simply prints a description of the @@ -153,9 +216,11 @@ class HashError(InstallationError): return ' {}'.format(self._requirement_name()) def __str__(self): + # type: () -> str return '{}\n{}'.format(self.head, self.body()) def _requirement_name(self): + # type: () -> str """Return a description of the requirement that triggered me. This default implementation returns long description of the req, with @@ -196,6 +261,7 @@ class HashMissing(HashError): 'has a hash.)') def __init__(self, gotten_hash): + # type: (str) -> None """ :param gotten_hash: The hash of the (possibly malicious) archive we just downloaded @@ -203,6 +269,7 @@ class HashMissing(HashError): self.gotten_hash = gotten_hash def body(self): + # type: () -> str # Dodge circular import. from pip._internal.utils.hashes import FAVORITE_HASH @@ -245,6 +312,7 @@ class HashMismatch(HashError): 'someone may have tampered with them.') def __init__(self, allowed, gots): + # type: (Dict[str, List[str]], Dict[str, _Hash]) -> None """ :param allowed: A dict of algorithm names pointing to lists of allowed hex digests @@ -255,10 +323,12 @@ class HashMismatch(HashError): self.gots = gots def body(self): + # type: () -> str return ' {}:\n{}'.format(self._requirement_name(), self._hash_comparison()) def _hash_comparison(self): + # type: () -> str """ Return a comparison of actual and expected hash values. @@ -270,11 +340,12 @@ class HashMismatch(HashError): """ def hash_then_or(hash_name): + # type: (str) -> chain[str] # For now, all the decent hashes have 6-char names, so we can get # away with hard-coding space literals. return chain([hash_name], repeat(' or')) - lines = [] + lines = [] # type: List[str] for hash_name, expecteds in iteritems(self.allowed): prefix = hash_then_or(hash_name) lines.extend((' Expected {} {}'.format(next(prefix), e)) @@ -294,15 +365,17 @@ class ConfigurationFileCouldNotBeLoaded(ConfigurationError): """ def __init__(self, reason="could not be loaded", fname=None, error=None): + # type: (str, Optional[str], Optional[configparser.Error]) -> None super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) self.reason = reason self.fname = fname self.error = error def __str__(self): + # type: () -> str if self.fname is not None: message_part = " in {}.".format(self.fname) else: assert self.error is not None - message_part = ".\n{}\n".format(self.error.message) + message_part = ".\n{}\n".format(self.error) return "Configuration file {}{}".format(self.reason, message_part) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py b/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py index e2c800c..6c35fc6 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py @@ -13,11 +13,14 @@ from collections import OrderedDict from pip._vendor import html5lib, requests from pip._vendor.distlib.compat import unescape -from pip._vendor.requests.exceptions import HTTPError, RetryError, SSLError +from pip._vendor.requests.exceptions import RetryError, SSLError from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib import request as urllib_request +from pip._internal.exceptions import NetworkConnectionError from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS from pip._internal.utils.misc import pairwise, redact_auth_from_url from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -25,6 +28,7 @@ from pip._internal.utils.urls import path_to_url, url_to_path from pip._internal.vcs import is_url, vcs if MYPY_CHECK_RUNNING: + from optparse import Values from typing import ( Callable, Iterable, List, MutableMapping, Optional, Protocol, Sequence, Tuple, TypeVar, Union, @@ -33,7 +37,6 @@ if MYPY_CHECK_RUNNING: from pip._vendor.requests import Response - from pip._internal.models.search_scope import SearchScope from pip._internal.network.session import PipSession HTMLElement = xml.etree.ElementTree.Element @@ -122,7 +125,7 @@ def _ensure_html_response(url, session): raise _NotHTTP() resp = session.head(url, allow_redirects=True) - resp.raise_for_status() + raise_for_status(resp) _ensure_html_header(resp) @@ -166,7 +169,7 @@ def _get_html_response(url, session): "Cache-Control": "max-age=0", }, ) - resp.raise_for_status() + raise_for_status(resp) # The check for archives above only works if the url ends with # something that looks like an archive. However that is not a @@ -434,7 +437,8 @@ def _get_html_page(link, session=None): # Check for VCS schemes that do not support lookup as web pages. vcs_scheme = _match_vcs_scheme(url) if vcs_scheme: - logger.debug('Cannot look at %s URL %s', vcs_scheme, link) + logger.warning('Cannot look at %s URL %s because it does not support ' + 'lookup as web pages.', vcs_scheme, link) return None # Tack index.html onto file:// URLs that point to directories @@ -450,16 +454,17 @@ def _get_html_page(link, session=None): try: resp = _get_html_response(url, session=session) except _NotHTTP: - logger.debug( + logger.warning( 'Skipping page %s because it looks like an archive, and cannot ' - 'be checked by HEAD.', link, + 'be checked by a HTTP HEAD request.', link, ) except _NotHTML as exc: - logger.debug( - 'Skipping page %s because the %s request got Content-Type: %s', + logger.warning( + 'Skipping page %s because the %s request got Content-Type: %s.' + 'The only supported Content-Type is text/html', link, exc.request_desc, exc.content_type, ) - except HTTPError as exc: + except NetworkConnectionError as exc: _handle_get_page_fail(link, exc) except RetryError as exc: _handle_get_page_fail(link, exc) @@ -524,8 +529,7 @@ def group_locations(locations, expand_dir=False): urls.append(url) else: logger.warning( - "Path '{0}' is ignored: " - "it is a directory.".format(path), + "Path '%s' is ignored: it is a directory.", path, ) elif os.path.isfile(path): sort_path(path) @@ -599,6 +603,33 @@ class LinkCollector(object): self.search_scope = search_scope self.session = session + @classmethod + def create(cls, session, options, suppress_no_index=False): + # type: (PipSession, Values, bool) -> LinkCollector + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + 'Ignoring indexes: %s', + ','.join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, index_urls=index_urls, + ) + link_collector = LinkCollector( + session=session, search_scope=search_scope, + ) + return link_collector + @property def find_links(self): # type: () -> List[str] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py b/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py index e88ad9f..8411578 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py @@ -556,23 +556,7 @@ class CandidateEvaluator(object): """ if not candidates: return None - best_candidate = max(candidates, key=self._sort_key) - - # Log a warning per PEP 592 if necessary before returning. - link = best_candidate.link - if link.is_yanked: - reason = link.yanked_reason or '' - msg = ( - # Mark this as a unicode string to prevent - # "UnicodeEncodeError: 'ascii' codec can't encode character" - # in Python 2 when the reason contains non-ascii characters. - u'The candidate selected for download or install is a ' - 'yanked version: {candidate}\n' - 'Reason for being yanked: {reason}' - ).format(candidate=best_candidate, reason=reason) - logger.warning(msg) - return best_candidate def compute_best_candidate( @@ -674,6 +658,11 @@ class PackageFinder(object): ignore_requires_python=selection_prefs.ignore_requires_python, ) + @property + def target_python(self): + # type: () -> TargetPython + return self._target_python + @property def search_scope(self): # type: () -> SearchScope @@ -709,6 +698,15 @@ class PackageFinder(object): # type: () -> None self._candidate_prefs.allow_all_prereleases = True + @property + def prefer_binary(self): + # type: () -> bool + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self): + # type: () -> None + self._candidate_prefs.prefer_binary = True + def make_link_evaluator(self, project_name): # type: (str) -> LinkEvaluator canonical_name = canonicalize_name(project_name) @@ -889,11 +887,11 @@ class PackageFinder(object): return candidate_evaluator.compute_best_candidate(candidates) def find_requirement(self, req, upgrade): - # type: (InstallRequirement, bool) -> Optional[Link] + # type: (InstallRequirement, bool) -> Optional[InstallationCandidate] """Try to find a Link matching req Expects req, an InstallRequirement and upgrade, a boolean - Returns a Link if found, + Returns a InstallationCandidate if found, Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise """ hashes = req.hashes(trust_internet=False) @@ -967,7 +965,7 @@ class PackageFinder(object): best_candidate.version, _format_versions(best_candidate_result.iter_applicable()), ) - return best_candidate.link + return best_candidate def _find_name_version_sep(fragment, canonical_name): diff --git a/venv/lib/python3.8/site-packages/pip/_internal/locations.py b/venv/lib/python3.8/site-packages/pip/_internal/locations.py index 0c11553..0c12354 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/locations.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/locations.py @@ -138,7 +138,7 @@ def distutils_scheme( if running_under_virtualenv(): scheme['headers'] = os.path.join( - sys.prefix, + i.prefix, 'include', 'site', 'python{}'.format(get_major_minor_version()), diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py b/venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py index 1dc1a57..9149e0f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py @@ -12,6 +12,8 @@ class InstallationCandidate(KeyBasedCompareMixin): """Represents a potential "candidate" for installation. """ + __slots__ = ["name", "version", "link"] + def __init__(self, name, version, link): # type: (str, str, Link) -> None self.name = name diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py b/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py index 2e13727..c6275e7 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import CommandError @@ -14,6 +11,8 @@ class FormatControl(object): """Helper for managing formats from which a package can be installed. """ + __slots__ = ["no_binary", "only_binary"] + def __init__(self, no_binary=None, only_binary=None): # type: (Optional[Set[str]], Optional[Set[str]]) -> None if no_binary is None: @@ -26,7 +25,16 @@ class FormatControl(object): def __eq__(self, other): # type: (object) -> bool - return self.__dict__ == other.__dict__ + if not isinstance(other, self.__class__): + return NotImplemented + + if self.__slots__ != other.__slots__: + return False + + return all( + getattr(self, k) == getattr(other, k) + for k in self.__slots__ + ) def __ne__(self, other): # type: (object) -> bool @@ -42,7 +50,7 @@ class FormatControl(object): @staticmethod def handle_mutual_excludes(value, target, other): - # type: (str, Optional[Set[str]], Optional[Set[str]]) -> None + # type: (str, Set[str], Set[str]) -> None if value.startswith('-'): raise CommandError( "--no-binary / --only-binary option requires 1 argument." diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/index.py b/venv/lib/python3.8/site-packages/pip/_internal/models/index.py index ead1efb..5b4a1fe 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/index.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/index.py @@ -5,6 +5,9 @@ class PackageIndex(object): """Represents a Package Index and provides easier access to endpoints """ + __slots__ = ['url', 'netloc', 'simple_url', 'pypi_url', + 'file_storage_domain'] + def __init__(self, url, file_storage_domain): # type: (str, str) -> None super(PackageIndex, self).__init__() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/link.py b/venv/lib/python3.8/site-packages/pip/_internal/models/link.py index df4f8f0..c0d278a 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/link.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/link.py @@ -24,6 +24,15 @@ class Link(KeyBasedCompareMixin): """Represents a parsed link from a Package Index's simple URL """ + __slots__ = [ + "_parsed_url", + "_url", + "comes_from", + "requires_python", + "yanked_reason", + "cache_link_parsing", + ] + def __init__( self, url, # type: str diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py b/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py index af07b40..5040551 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py @@ -6,10 +6,16 @@ https://docs.python.org/3/install/index.html#alternate-installation. """ +SCHEME_KEYS = ['platlib', 'purelib', 'headers', 'scripts', 'data'] + + class Scheme(object): """A Scheme holds paths which are used as the base directories for artifacts associated with a Python package. """ + + __slots__ = SCHEME_KEYS + def __init__( self, platlib, # type: str diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py b/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py index 7a0008e..d732504 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py @@ -24,6 +24,8 @@ class SearchScope(object): Encapsulates the locations that pip is configured to search. """ + __slots__ = ["find_links", "index_urls"] + @classmethod def create( cls, @@ -93,8 +95,8 @@ class SearchScope(object): # exceptions for malformed URLs if not purl.scheme and not purl.netloc: logger.warning( - 'The index url "{}" seems invalid, ' - 'please provide a scheme.'.format(redacted_index_url)) + 'The index url "%s" seems invalid, ' + 'please provide a scheme.', redacted_index_url) redacted_index_urls.append(redacted_index_url) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py b/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py index f58fdce..5db3ca9 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py @@ -6,12 +6,14 @@ if MYPY_CHECK_RUNNING: class SelectionPreferences(object): - """ Encapsulates the candidate selection preferences for downloading and installing files. """ + __slots__ = ['allow_yanked', 'allow_all_prereleases', 'format_control', + 'prefer_binary', 'ignore_requires_python'] + # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes # that decision to be made explicit in the calling code, which helps diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py b/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py index 84f1c20..6d1ca79 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py @@ -20,6 +20,16 @@ class TargetPython(object): for a package install, download, etc. """ + __slots__ = [ + "_given_py_version_info", + "abi", + "implementation", + "platform", + "py_version", + "py_version_info", + "_valid_tags", + ] + def __init__( self, platform=None, # type: Optional[str] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py b/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py index 94da3d4..c49deaa 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py @@ -4,9 +4,6 @@ Contains interface (MultiDomainBasicAuth) and associated glue code for providing credentials in the context of network requests. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth @@ -23,11 +20,12 @@ from pip._internal.utils.misc import ( from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Dict, Optional, Tuple + from typing import Dict, Optional, Tuple, List, Any from pip._internal.vcs.versioncontrol import AuthInfo + from pip._vendor.requests.models import Response, Request + Credentials = Tuple[str, str, str] logger = logging.getLogger(__name__) @@ -44,7 +42,9 @@ except Exception as exc: def get_keyring_auth(url, username): + # type: (str, str) -> Optional[AuthInfo] """Return the tuple auth for a given url from keyring.""" + global keyring if not url or not keyring: return None @@ -70,12 +70,14 @@ def get_keyring_auth(url, username): logger.warning( "Keyring is skipped due to an exception: %s", str(exc), ) + keyring = None + return None class MultiDomainBasicAuth(AuthBase): def __init__(self, prompting=True, index_urls=None): - # type: (bool, Optional[Values]) -> None + # type: (bool, Optional[List[str]]) -> None self.prompting = prompting self.index_urls = index_urls self.passwords = {} # type: Dict[str, AuthInfo] @@ -87,6 +89,7 @@ class MultiDomainBasicAuth(AuthBase): self._credentials_to_save = None # type: Optional[Credentials] def _get_index_url(self, url): + # type: (str) -> Optional[str] """Return the original index URL matching the requested URL. Cached or dynamically generated credentials may work against @@ -106,9 +109,11 @@ class MultiDomainBasicAuth(AuthBase): prefix = remove_auth_from_url(u).rstrip("/") + "/" if url.startswith(prefix): return u + return None def _get_new_credentials(self, original_url, allow_netrc=True, allow_keyring=True): + # type: (str, bool, bool) -> AuthInfo """Find and return credentials for the specified URL.""" # Split the credentials and netloc from the url. url, netloc, url_user_password = split_auth_netloc_from_url( @@ -158,6 +163,7 @@ class MultiDomainBasicAuth(AuthBase): return username, password def _get_url_and_credentials(self, original_url): + # type: (str) -> Tuple[str, Optional[str], Optional[str]] """Return the credentials to use for the provided URL. If allowed, netrc and keyring may be used to obtain the @@ -198,6 +204,7 @@ class MultiDomainBasicAuth(AuthBase): return url, username, password def __call__(self, req): + # type: (Request) -> Request # Get credentials for this request url, username, password = self._get_url_and_credentials(req.url) @@ -215,22 +222,25 @@ class MultiDomainBasicAuth(AuthBase): # Factored out to allow for easy patching in tests def _prompt_for_password(self, netloc): + # type: (str) -> Tuple[Optional[str], Optional[str], bool] username = ask_input("User for {}: ".format(netloc)) if not username: - return None, None + return None, None, False auth = get_keyring_auth(netloc, username) - if auth: + if auth and auth[0] is not None and auth[1] is not None: return auth[0], auth[1], False password = ask_password("Password: ") return username, password, True # Factored out to allow for easy patching in tests def _should_save_password_to_keyring(self): + # type: () -> bool if not keyring: return False return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" def handle_401(self, resp, **kwargs): + # type: (Response, **Any) -> Response # We only care about 401 responses, anything else we want to just # pass through the actual response if resp.status_code != 401: @@ -276,6 +286,7 @@ class MultiDomainBasicAuth(AuthBase): return new_resp def warn_on_401(self, resp, **kwargs): + # type: (Response, **Any) -> None """Response callback to warn about incorrect credentials.""" if resp.status_code == 401: logger.warning( @@ -283,6 +294,7 @@ class MultiDomainBasicAuth(AuthBase): ) def save_credentials(self, resp, **kwargs): + # type: (Response, **Any) -> None """Response callback to save credentials on success.""" assert keyring is not None, "should never reach here without keyring" if not keyring: diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py index c9386e1..a0d55b5 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py @@ -1,9 +1,6 @@ """HTTP cache implementation. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import os from contextlib import contextmanager @@ -16,7 +13,7 @@ from pip._internal.utils.misc import ensure_dir from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Optional + from typing import Optional, Iterator def is_from_cache(response): @@ -26,6 +23,7 @@ def is_from_cache(response): @contextmanager def suppressed_cache_errors(): + # type: () -> Iterator[None] """If we can't access the cache then we can just skip caching and process requests as if caching wasn't enabled. """ diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/download.py b/venv/lib/python3.8/site-packages/pip/_internal/network/download.py index 2f3e08a..44f9985 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/network/download.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/download.py @@ -5,13 +5,17 @@ import logging import mimetypes import os -from pip._vendor import requests from pip._vendor.requests.models import CONTENT_CHUNK_SIZE from pip._internal.cli.progress_bars import DownloadProgressProvider +from pip._internal.exceptions import NetworkConnectionError from pip._internal.models.index import PyPI from pip._internal.network.cache import is_from_cache -from pip._internal.network.utils import response_chunks +from pip._internal.network.utils import ( + HEADERS, + raise_for_status, + response_chunks, +) from pip._internal.utils.misc import ( format_size, redact_auth_from_url, @@ -132,31 +136,8 @@ def _get_http_response_filename(resp, link): def _http_get_download(session, link): # type: (PipSession, Link) -> Response target_url = link.url.split('#', 1)[0] - resp = session.get( - target_url, - # We use Accept-Encoding: identity here because requests - # defaults to accepting compressed responses. This breaks in - # a variety of ways depending on how the server is configured. - # - Some servers will notice that the file isn't a compressible - # file and will leave the file alone and with an empty - # Content-Encoding - # - Some servers will notice that the file is already - # compressed and will leave the file alone and will add a - # Content-Encoding: gzip header - # - Some servers won't notice anything at all and will take - # a file that's already been compressed and compress it again - # and set the Content-Encoding: gzip header - # By setting this to request only the identity encoding We're - # hoping to eliminate the third case. Hopefully there does not - # exist a server which when given a file will notice it is - # already compressed and that you're not asking for a - # compressed file and will then decompress it before sending - # because if that's the case I don't think it'll ever be - # possible to make this work. - headers={"Accept-Encoding": "identity"}, - stream=True, - ) - resp.raise_for_status() + resp = session.get(target_url, headers=HEADERS, stream=True) + raise_for_status(resp) return resp @@ -187,7 +168,8 @@ class Downloader(object): # type: (Link) -> Download try: resp = _http_get_download(self._session, link) - except requests.HTTPError as e: + except NetworkConnectionError as e: + assert e.response is not None logger.critical( "HTTP error %s while getting %s", e.response.status_code, link ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/lazy_wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/network/lazy_wheel.py new file mode 100644 index 0000000..a0f9e15 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/lazy_wheel.py @@ -0,0 +1,235 @@ +"""Lazy ZIP over HTTP""" + +__all__ = ['HTTPRangeRequestUnsupported', 'dist_from_wheel_url'] + +from bisect import bisect_left, bisect_right +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from zipfile import BadZipfile, ZipFile + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.six.moves import range + +from pip._internal.network.utils import ( + HEADERS, + raise_for_status, + response_chunks, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, List, Optional, Tuple + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Response + + from pip._internal.network.session import PipSession + + +class HTTPRangeRequestUnsupported(Exception): + pass + + +def dist_from_wheel_url(name, url, session): + # type: (str, str, PipSession) -> Distribution + """Return a pkg_resources.Distribution from the given wheel URL. + + This uses HTTP range requests to only fetch the potion of the wheel + containing metadata, just enough for the object to be constructed. + If such requests are not supported, HTTPRangeRequestUnsupported + is raised. + """ + with LazyZipOverHTTP(url, session) as wheel: + # For read-only ZIP files, ZipFile only needs methods read, + # seek, seekable and tell, not the whole IO protocol. + zip_file = ZipFile(wheel) # type: ignore + # After context manager exit, wheel.name + # is an invalid file by intention. + return pkg_resources_distribution_for_wheel(zip_file, name, wheel.name) + + +class LazyZipOverHTTP(object): + """File-like object mapped to a ZIP file over HTTP. + + This uses HTTP range requests to lazily fetch the file's content, + which is supposed to be fed to ZipFile. If such requests are not + supported by the server, raise HTTPRangeRequestUnsupported + during initialization. + """ + + def __init__(self, url, session, chunk_size=CONTENT_CHUNK_SIZE): + # type: (str, PipSession, int) -> None + head = session.head(url, headers=HEADERS) + raise_for_status(head) + assert head.status_code == 200 + self._session, self._url, self._chunk_size = session, url, chunk_size + self._length = int(head.headers['Content-Length']) + self._file = NamedTemporaryFile() + self.truncate(self._length) + self._left = [] # type: List[int] + self._right = [] # type: List[int] + if 'bytes' not in head.headers.get('Accept-Ranges', 'none'): + raise HTTPRangeRequestUnsupported('range request is not supported') + self._check_zip() + + @property + def mode(self): + # type: () -> str + """Opening mode, which is always rb.""" + return 'rb' + + @property + def name(self): + # type: () -> str + """Path to the underlying file.""" + return self._file.name + + def seekable(self): + # type: () -> bool + """Return whether random access is supported, which is True.""" + return True + + def close(self): + # type: () -> None + """Close the file.""" + self._file.close() + + @property + def closed(self): + # type: () -> bool + """Whether the file is closed.""" + return self._file.closed + + def read(self, size=-1): + # type: (int) -> bytes + """Read up to size bytes from the object and return them. + + As a convenience, if size is unspecified or -1, + all bytes until EOF are returned. Fewer than + size bytes may be returned if EOF is reached. + """ + download_size = max(size, self._chunk_size) + start, length = self.tell(), self._length + stop = length if size < 0 else min(start+download_size, length) + start = max(0, stop-download_size) + self._download(start, stop-1) + return self._file.read(size) + + def readable(self): + # type: () -> bool + """Return whether the file is readable, which is True.""" + return True + + def seek(self, offset, whence=0): + # type: (int, int) -> int + """Change stream position and return the new absolute position. + + Seek to offset relative position indicated by whence: + * 0: Start of stream (the default). pos should be >= 0; + * 1: Current position - pos may be negative; + * 2: End of stream - pos usually negative. + """ + return self._file.seek(offset, whence) + + def tell(self): + # type: () -> int + """Return the current possition.""" + return self._file.tell() + + def truncate(self, size=None): + # type: (Optional[int]) -> int + """Resize the stream to the given size in bytes. + + If size is unspecified resize to the current position. + The current stream position isn't changed. + + Return the new file size. + """ + return self._file.truncate(size) + + def writable(self): + # type: () -> bool + """Return False.""" + return False + + def __enter__(self): + # type: () -> LazyZipOverHTTP + self._file.__enter__() + return self + + def __exit__(self, *exc): + # type: (*Any) -> Optional[bool] + return self._file.__exit__(*exc) + + @contextmanager + def _stay(self): + # type: ()-> Iterator[None] + """Return a context manager keeping the position. + + At the end of the block, seek back to original position. + """ + pos = self.tell() + try: + yield + finally: + self.seek(pos) + + def _check_zip(self): + # type: () -> None + """Check and download until the file is a valid ZIP.""" + end = self._length - 1 + for start in reversed(range(0, end, self._chunk_size)): + self._download(start, end) + with self._stay(): + try: + # For read-only ZIP files, ZipFile only needs + # methods read, seek, seekable and tell. + ZipFile(self) # type: ignore + except BadZipfile: + pass + else: + break + + def _stream_response(self, start, end, base_headers=HEADERS): + # type: (int, int, Dict[str, str]) -> Response + """Return HTTP response to a range request from start to end.""" + headers = base_headers.copy() + headers['Range'] = 'bytes={}-{}'.format(start, end) + # TODO: Get range requests to be correctly cached + headers['Cache-Control'] = 'no-cache' + return self._session.get(self._url, headers=headers, stream=True) + + def _merge(self, start, end, left, right): + # type: (int, int, int, int) -> Iterator[Tuple[int, int]] + """Return an iterator of intervals to be fetched. + + Args: + start (int): Start of needed interval + end (int): End of needed interval + left (int): Index of first overlapping downloaded data + right (int): Index after last overlapping downloaded data + """ + lslice, rslice = self._left[left:right], self._right[left:right] + i = start = min([start]+lslice[:1]) + end = max([end]+rslice[-1:]) + for j, k in zip(lslice, rslice): + if j > i: + yield i, j-1 + i = k + 1 + if i <= end: + yield i, end + self._left[left:right], self._right[left:right] = [start], [end] + + def _download(self, start, end): + # type: (int, int) -> None + """Download bytes from start to end inclusively.""" + with self._stay(): + left = bisect_left(self._right, start) + right = bisect_right(self._left, end) + for start, end in self._merge(start, end, left, right): + response = self._stream_response(start, end) + response.raise_for_status() + self.seek(start) + for chunk in response_chunks(response, self._chunk_size): + self._file.write(chunk) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py b/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py index a19050b..907b3fe 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py @@ -1,9 +1,58 @@ from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from pip._internal.exceptions import NetworkConnectionError from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Iterator + from typing import Dict, Iterator + +# The following comments and HTTP headers were originally added by +# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. +# +# We use Accept-Encoding: identity here because requests defaults to +# accepting compressed responses. This breaks in a variety of ways +# depending on how the server is configured. +# - Some servers will notice that the file isn't a compressible file +# and will leave the file alone and with an empty Content-Encoding +# - Some servers will notice that the file is already compressed and +# will leave the file alone, adding a Content-Encoding: gzip header +# - Some servers won't notice anything at all and will take a file +# that's already been compressed and compress it again, and set +# the Content-Encoding: gzip header +# By setting this to request only the identity encoding we're hoping +# to eliminate the third case. Hopefully there does not exist a server +# which when given a file will notice it is already compressed and that +# you're not asking for a compressed file and will then decompress it +# before sending because if that's the case I don't think it'll ever be +# possible to make this work. +HEADERS = {'Accept-Encoding': 'identity'} # type: Dict[str, str] + + +def raise_for_status(resp): + # type: (Response) -> None + http_error_msg = u'' + if isinstance(resp.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. + try: + reason = resp.reason.decode('utf-8') + except UnicodeDecodeError: + reason = resp.reason.decode('iso-8859-1') + else: + reason = resp.reason + + if 400 <= resp.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + elif 500 <= resp.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + if http_error_msg: + raise NetworkConnectionError(http_error_msg, response=resp) def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE): diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py b/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py index 121edd9..e611262 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py @@ -1,17 +1,22 @@ """xmlrpclib.Transport implementation """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging -from pip._vendor import requests # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is # why we ignore the type on this import from pip._vendor.six.moves import xmlrpc_client # type: ignore from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict + from pip._internal.network.session import PipSession + + logger = logging.getLogger(__name__) @@ -21,22 +26,25 @@ class PipXmlrpcTransport(xmlrpc_client.Transport): """ def __init__(self, index_url, session, use_datetime=False): + # type: (str, PipSession, bool) -> None xmlrpc_client.Transport.__init__(self, use_datetime) index_parts = urllib_parse.urlparse(index_url) self._scheme = index_parts.scheme self._session = session def request(self, host, handler, request_body, verbose=False): + # type: (str, str, Dict[str, str], bool) -> None parts = (self._scheme, host, handler, None, None, None) url = urllib_parse.urlunparse(parts) try: headers = {'Content-Type': 'text/xml'} response = self._session.post(url, data=request_body, headers=headers, stream=True) - response.raise_for_status() + raise_for_status(response) self.verbose = verbose return self.parse_response(response.raw) - except requests.HTTPError as exc: + except NetworkConnectionError as exc: + assert exc.response logger.critical( "HTTP error %s while getting %s", exc.response.status_code, url, diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py index b13fbde..cf52f8d 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py @@ -1,7 +1,6 @@ """Metadata generation logic for source distributions. """ -import logging import os from pip._internal.utils.subprocess import runner_with_spinner_message @@ -12,8 +11,6 @@ if MYPY_CHECK_RUNNING: from pip._internal.build_env import BuildEnvironment from pip._vendor.pep517.wrappers import Pep517HookCaller -logger = logging.getLogger(__name__) - def generate_metadata(build_env, backend): # type: (BuildEnvironment, Pep517HookCaller) -> str diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py index 1266ce0..0c28c49 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py @@ -27,7 +27,7 @@ def build_wheel_pep517( if build_options: # PEP 517 does not support --build-options logger.error('Cannot build wheel for %s using PEP 517 when ' - '--build-option is present' % (name,)) + '--build-option is present', name) return None try: logger.debug('Destination directory: %s', tempd) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py index b85a123..5714915 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py @@ -1,10 +1,6 @@ """Validation of dependencies of packages """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - import logging from collections import namedtuple @@ -33,6 +29,7 @@ if MYPY_CHECK_RUNNING: MissingDict = Dict[str, List[Missing]] ConflictingDict = Dict[str, List[Conflicting]] CheckResult = Tuple[MissingDict, ConflictingDict] + ConflictDetails = Tuple[PackageSet, CheckResult] PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) @@ -65,9 +62,6 @@ def check_package_set(package_set, should_ignore=None): If should_ignore is passed, it should be a callable that takes a package name and returns a boolean. """ - if should_ignore is None: - def should_ignore(name): - return False missing = {} conflicting = {} @@ -77,7 +71,7 @@ def check_package_set(package_set, should_ignore=None): missing_deps = set() # type: Set[Missing] conflicting_deps = set() # type: Set[Conflicting] - if should_ignore(package_name): + if should_ignore and should_ignore(package_name): continue for req in package_set[package_name].requires: @@ -106,7 +100,7 @@ def check_package_set(package_set, should_ignore=None): def check_install_conflicts(to_install): - # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] + # type: (List[InstallRequirement]) -> ConflictDetails """For checking if the dependency graph would be consistent after \ installing given requirements """ @@ -139,6 +133,7 @@ def _simulate_installation_of(to_install, package_set): abstract_dist = make_distribution_for_install_requirement(inst_req) dist = abstract_dist.get_pkg_resources_distribution() + assert dist is not None name = canonicalize_name(dist.key) package_set[name] = PackageDetails(dist.version, dist.requires()) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py index aa6b052..ddb9cb2 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py @@ -1,7 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import collections @@ -46,8 +42,8 @@ logger = logging.getLogger(__name__) def freeze( requirement=None, # type: Optional[List[str]] find_links=None, # type: Optional[List[str]] - local_only=None, # type: Optional[bool] - user_only=None, # type: Optional[bool] + local_only=False, # type: bool + user_only=False, # type: bool paths=None, # type: Optional[List[str]] isolated=False, # type: bool wheel_cache=None, # type: Optional[WheelCache] @@ -60,10 +56,13 @@ def freeze( for link in find_links: yield '-f {}'.format(link) installations = {} # type: Dict[str, FrozenRequirement] - for dist in get_installed_distributions(local_only=local_only, - skip=(), - user_only=user_only, - paths=paths): + + for dist in get_installed_distributions( + local_only=local_only, + skip=(), + user_only=user_only, + paths=paths + ): try: req = FrozenRequirement.from_dist(dist) except RequirementParseError as exc: @@ -96,13 +95,13 @@ def freeze( line.strip().startswith('#') or line.startswith(( '-r', '--requirement', - '-Z', '--always-unzip', '-f', '--find-links', '-i', '--index-url', '--pre', '--trusted-host', '--process-dependency-links', - '--extra-index-url'))): + '--extra-index-url', + '--use-feature'))): line = line.rstrip() if line not in emitted_options: emitted_options.add(line) @@ -266,6 +265,7 @@ class FrozenRequirement(object): return cls(dist.project_name, req, editable, comments=comments) def __str__(self): + # type: () -> str req = self.req if self.editable: req = '-e {}'.format(req) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py index 0fac905..87227d5 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py @@ -6,7 +6,7 @@ import os import sys from distutils.util import change_root -from pip._internal.utils.deprecation import deprecated +from pip._internal.exceptions import InstallationError from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ensure_dir from pip._internal.utils.setuptools_build import make_setuptools_install_args @@ -106,24 +106,12 @@ def install( egg_info_dir = prepend_root(directory) break else: - deprecated( - reason=( - "{} did not indicate that it installed an " - ".egg-info directory. Only setup.py projects " - "generating .egg-info directories are supported." - ).format(req_description), - replacement=( - "for maintainers: updating the setup.py of {0}. " - "For users: contact the maintainers of {0} to let " - "them know to update their setup.py.".format( - req_name - ) - ), - gone_in="20.2", - issue=6998, - ) - # FIXME: put the record somewhere - return True + message = ( + "{} did not indicate that it installed an " + ".egg-info directory. Only setup.py projects " + "generating .egg-info directories are supported." + ).format(req_description) + raise InstallationError(message) new_lines = [] for line in record_lines: diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py index 2fb86b8..e91b1b8 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py @@ -1,64 +1,108 @@ """Support for installing and building the "wheel" binary package format. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import import collections import compileall import contextlib import csv +import importlib import logging import os.path import re import shutil -import stat import sys import warnings from base64 import urlsafe_b64encode -from itertools import starmap +from itertools import chain, starmap from zipfile import ZipFile from pip._vendor import pkg_resources from pip._vendor.distlib.scripts import ScriptMaker from pip._vendor.distlib.util import get_export_entry -from pip._vendor.six import StringIO +from pip._vendor.six import ( + PY2, + ensure_str, + ensure_text, + itervalues, + reraise, + text_type, +) +from pip._vendor.six.moves import filterfalse, map from pip._internal.exceptions import InstallationError from pip._internal.locations import get_major_minor_version from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.models.scheme import SCHEME_KEYS from pip._internal.utils.filesystem import adjacent_tmp_file, replace -from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file -from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.misc import ( + captured_stdout, + ensure_dir, + hash_file, + partition, +) from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.utils.unpacking import current_umask, unpack_file -from pip._internal.utils.wheel import parse_wheel +from pip._internal.utils.unpacking import ( + current_umask, + is_within_directory, + set_extracted_file_to_default_mode_plus_executable, + zip_item_is_executable, +) +from pip._internal.utils.wheel import ( + parse_wheel, + pkg_resources_distribution_for_wheel, +) -if MYPY_CHECK_RUNNING: +# Use the custom cast function at runtime to make cast work, +# and import typing.cast when performing pre-commit and type +# checks +if not MYPY_CHECK_RUNNING: + from pip._internal.utils.typing import cast +else: from email.message import Message from typing import ( - Dict, List, Optional, Sequence, Tuple, Any, - Iterable, Iterator, Callable, Set, + Any, + Callable, + Dict, + IO, + Iterable, + Iterator, + List, + NewType, + Optional, + Protocol, + Sequence, + Set, + Tuple, + Union, + cast, ) + from zipfile import ZipInfo + + from pip._vendor.pkg_resources import Distribution from pip._internal.models.scheme import Scheme from pip._internal.utils.filesystem import NamedTemporaryFileResult - InstalledCSVRow = Tuple[str, ...] + RecordPath = NewType('RecordPath', text_type) + InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] + + class File(Protocol): + src_record_path = None # type: RecordPath + dest_path = None # type: text_type + changed = None # type: bool + + def save(self): + # type: () -> None + pass logger = logging.getLogger(__name__) -def normpath(src, p): - # type: (str, str) -> str - return os.path.relpath(src, p).replace(os.path.sep, '/') - - def rehash(path, blocksize=1 << 20): - # type: (str, int) -> Tuple[str, str] + # type: (text_type, int) -> Tuple[str, str] """Return (encoded_digest, length) for path using hashlib.sha256()""" h, length = hash_file(path, blocksize) digest = 'sha256=' + urlsafe_b64encode( @@ -73,31 +117,31 @@ def csv_io_kwargs(mode): """Return keyword arguments to properly open a CSV file in the given mode. """ - if sys.version_info.major < 3: + if PY2: return {'mode': '{}b'.format(mode)} else: - return {'mode': mode, 'newline': ''} + return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} def fix_script(path): - # type: (str) -> Optional[bool] + # type: (text_type) -> bool """Replace #!python with #!/path/to/python Return True if file was changed. """ # XXX RECORD hashes will need to be updated - if os.path.isfile(path): - with open(path, 'rb') as script: - firstline = script.readline() - if not firstline.startswith(b'#!python'): - return False - exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = b'#!' + exename + os.linesep.encode("ascii") - rest = script.read() - with open(path, 'wb') as script: - script.write(firstline) - script.write(rest) - return True - return None + assert os.path.isfile(path) + + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True def wheel_root_is_purelib(metadata): @@ -105,26 +149,16 @@ def wheel_root_is_purelib(metadata): return metadata.get("Root-Is-Purelib", "").lower() == "true" -def get_entrypoints(filename): - # type: (str) -> Tuple[Dict[str, str], Dict[str, str]] - if not os.path.exists(filename): - return {}, {} - - # This is done because you can pass a string to entry_points wrappers which - # means that they may or may not be valid INI files. The attempt here is to - # strip leading and trailing whitespace in order to make them valid INI - # files. - with open(filename) as fp: - data = StringIO() - for line in fp: - data.write(line.strip()) - data.write("\n") - data.seek(0) - +def get_entrypoints(distribution): + # type: (Distribution) -> Tuple[Dict[str, str], Dict[str, str]] # get the entry points and then the script names - entry_points = pkg_resources.EntryPoint.parse_map(data) - console = entry_points.get('console_scripts', {}) - gui = entry_points.get('gui_scripts', {}) + try: + console = distribution.get_entry_map('console_scripts') + gui = distribution.get_entry_map('gui_scripts') + except KeyError: + # Our dict-based Distribution raises KeyError if entry_points.txt + # doesn't exist. + return {}, {} def _split_ep(s): # type: (pkg_resources.EntryPoint) -> Tuple[str, str] @@ -211,9 +245,12 @@ def message_about_scripts_not_on_PATH(scripts): return "\n".join(msg_lines) -def sorted_outrows(outrows): - # type: (Iterable[InstalledCSVRow]) -> List[InstalledCSVRow] - """Return the given rows of a RECORD file in sorted order. +def _normalized_outrows(outrows): + # type: (Iterable[InstalledCSVRow]) -> List[Tuple[str, str, str]] + """Normalize the given rows of a RECORD file. + + Items in each row are converted into str. Rows are then sorted to make + the value more predictable for tests. Each row is a 3-tuple (path, hash, size) and corresponds to a record of a RECORD file (see PEP 376 and PEP 427 for details). For the rows @@ -228,13 +265,39 @@ def sorted_outrows(outrows): # coerce each element to a string to avoid a TypeError in this case. # For additional background, see-- # https://github.com/pypa/pip/issues/5868 - return sorted(outrows, key=lambda row: tuple(str(x) for x in row)) + return sorted( + (ensure_str(record_path, encoding='utf-8'), hash_, str(size)) + for record_path, hash_, size in outrows + ) + + +def _record_to_fs_path(record_path): + # type: (RecordPath) -> text_type + return record_path + + +def _fs_to_record_path(path, relative_to=None): + # type: (text_type, Optional[text_type]) -> RecordPath + if relative_to is not None: + # On Windows, do not handle relative paths if they belong to different + # logical disks + if os.path.splitdrive(path)[0].lower() == \ + os.path.splitdrive(relative_to)[0].lower(): + path = os.path.relpath(path, relative_to) + path = path.replace(os.path.sep, '/') + return cast('RecordPath', path) + + +def _parse_record_path(record_column): + # type: (str) -> RecordPath + p = ensure_text(record_column, encoding='utf-8') + return cast('RecordPath', p) def get_csv_rows_for_installed( - old_csv_rows, # type: Iterable[List[str]] - installed, # type: Dict[str, str] - changed, # type: Set[str] + old_csv_rows, # type: List[List[str]] + installed, # type: Dict[RecordPath, RecordPath] + changed, # type: Set[RecordPath] generated, # type: List[str] lib_dir, # type: str ): @@ -246,227 +309,32 @@ def get_csv_rows_for_installed( installed_rows = [] # type: List[InstalledCSVRow] for row in old_csv_rows: if len(row) > 3: - logger.warning( - 'RECORD line has more than three elements: {}'.format(row) - ) - # Make a copy because we are mutating the row. - row = list(row) - old_path = row[0] - new_path = installed.pop(old_path, old_path) - row[0] = new_path - if new_path in changed: - digest, length = rehash(new_path) - row[1] = digest - row[2] = length - installed_rows.append(tuple(row)) + logger.warning('RECORD line has more than three elements: %s', row) + old_record_path = _parse_record_path(row[0]) + new_record_path = installed.pop(old_record_path, old_record_path) + if new_record_path in changed: + digest, length = rehash(_record_to_fs_path(new_record_path)) + else: + digest = row[1] if len(row) > 1 else '' + length = row[2] if len(row) > 2 else '' + installed_rows.append((new_record_path, digest, length)) for f in generated: + path = _fs_to_record_path(f, lib_dir) digest, length = rehash(f) - installed_rows.append((normpath(f, lib_dir), digest, str(length))) - for f in installed: - installed_rows.append((installed[f], '', '')) + installed_rows.append((path, digest, length)) + for installed_record_path in itervalues(installed): + installed_rows.append((installed_record_path, '', '')) return installed_rows -class MissingCallableSuffix(Exception): - pass - - -def _raise_for_invalid_entrypoint(specification): - # type: (str) -> None - entry = get_export_entry(specification) - if entry is not None and entry.suffix is None: - raise MissingCallableSuffix(str(entry)) - - -class PipScriptMaker(ScriptMaker): - def make(self, specification, options=None): - # type: (str, Dict[str, Any]) -> List[str] - _raise_for_invalid_entrypoint(specification) - return super(PipScriptMaker, self).make(specification, options) - - -def install_unpacked_wheel( - name, # type: str - wheeldir, # type: str - wheel_zip, # type: ZipFile - scheme, # type: Scheme - req_description, # type: str - pycompile=True, # type: bool - warn_script_location=True, # type: bool - direct_url=None, # type: Optional[DirectUrl] -): - # type: (...) -> None - """Install a wheel. - - :param name: Name of the project to install - :param wheeldir: Base directory of the unpacked wheel - :param wheel_zip: open ZipFile for wheel being installed - :param scheme: Distutils scheme dictating the install directories - :param req_description: String used in place of the requirement, for - logging - :param pycompile: Whether to byte-compile installed Python files - :param warn_script_location: Whether to check that scripts are installed - into a directory on PATH - :raises UnsupportedWheel: - * when the directory holds an unpacked wheel with incompatible - Wheel-Version - * when the .dist-info dir does not match the wheel +def get_console_script_specs(console): + # type: (Dict[str, str]) -> List[str] """ - # TODO: Investigate and break this up. - # TODO: Look into moving this into a dedicated class for representing an - # installation. - - source = wheeldir.rstrip(os.path.sep) + os.path.sep - - info_dir, metadata = parse_wheel(wheel_zip, name) - - if wheel_root_is_purelib(metadata): - lib_dir = scheme.purelib - else: - lib_dir = scheme.platlib - - subdirs = os.listdir(source) - data_dirs = [s for s in subdirs if s.endswith('.data')] - - # Record details of the files moved - # installed = files copied from the wheel to the destination - # changed = files changed while installing (scripts #! line typically) - # generated = files newly generated during the install (script wrappers) - installed = {} # type: Dict[str, str] - changed = set() - generated = [] # type: List[str] - - # Compile all of the pyc files that we're going to be installing - if pycompile: - with captured_stdout() as stdout: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore') - compileall.compile_dir(source, force=True, quiet=True) - logger.debug(stdout.getvalue()) - - def record_installed(srcfile, destfile, modified=False): - # type: (str, str, bool) -> None - """Map archive RECORD paths to installation RECORD paths.""" - oldpath = normpath(srcfile, wheeldir) - newpath = normpath(destfile, lib_dir) - installed[oldpath] = newpath - if modified: - changed.add(destfile) - - def clobber( - source, # type: str - dest, # type: str - is_base, # type: bool - fixer=None, # type: Optional[Callable[[str], Any]] - filter=None # type: Optional[Callable[[str], bool]] - ): - # type: (...) -> None - ensure_dir(dest) # common for the 'include' path - - for dir, subdirs, files in os.walk(source): - basedir = dir[len(source):].lstrip(os.path.sep) - destdir = os.path.join(dest, basedir) - if is_base and basedir == '': - subdirs[:] = [s for s in subdirs if not s.endswith('.data')] - for f in files: - # Skip unwanted files - if filter and filter(f): - continue - srcfile = os.path.join(dir, f) - destfile = os.path.join(dest, basedir, f) - # directory creation is lazy and after the file filtering above - # to ensure we don't install empty dirs; empty dirs can't be - # uninstalled. - ensure_dir(destdir) - - # copyfile (called below) truncates the destination if it - # exists and then writes the new contents. This is fine in most - # cases, but can cause a segfault if pip has loaded a shared - # object (e.g. from pyopenssl through its vendored urllib3) - # Since the shared object is mmap'd an attempt to call a - # symbol in it will then cause a segfault. Unlinking the file - # allows writing of new contents while allowing the process to - # continue to use the old copy. - if os.path.exists(destfile): - os.unlink(destfile) - - # We use copyfile (not move, copy, or copy2) to be extra sure - # that we are not moving directories over (copyfile fails for - # directories) as well as to ensure that we are not copying - # over any metadata because we want more control over what - # metadata we actually copy over. - shutil.copyfile(srcfile, destfile) - - # Copy over the metadata for the file, currently this only - # includes the atime and mtime. - st = os.stat(srcfile) - if hasattr(os, "utime"): - os.utime(destfile, (st.st_atime, st.st_mtime)) - - # If our file is executable, then make our destination file - # executable. - if os.access(srcfile, os.X_OK): - st = os.stat(srcfile) - permissions = ( - st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - ) - os.chmod(destfile, permissions) - - changed = False - if fixer: - changed = fixer(destfile) - record_installed(srcfile, destfile, changed) - - clobber(source, lib_dir, True) - - dest_info_dir = os.path.join(lib_dir, info_dir) - - # Get the defined entry points - ep_file = os.path.join(dest_info_dir, 'entry_points.txt') - console, gui = get_entrypoints(ep_file) - - def is_entrypoint_wrapper(name): - # type: (str) -> bool - # EP, EP.exe and EP-script.py are scripts generated for - # entry point EP by setuptools - if name.lower().endswith('.exe'): - matchname = name[:-4] - elif name.lower().endswith('-script.py'): - matchname = name[:-10] - elif name.lower().endswith(".pya"): - matchname = name[:-4] - else: - matchname = name - # Ignore setuptools-generated scripts - return (matchname in console or matchname in gui) - - for datadir in data_dirs: - fixer = None - filter = None - for subdir in os.listdir(os.path.join(wheeldir, datadir)): - fixer = None - if subdir == 'scripts': - fixer = fix_script - filter = is_entrypoint_wrapper - source = os.path.join(wheeldir, datadir, subdir) - dest = getattr(scheme, subdir) - clobber(source, dest, False, fixer=fixer, filter=filter) - - maker = PipScriptMaker(None, scheme.scripts) - - # Ensure old scripts are overwritten. - # See https://github.com/pypa/pip/issues/1800 - maker.clobber = True - - # Ensure we don't generate any variants for scripts because this is almost - # never what somebody wants. - # See https://bitbucket.org/pypa/distlib/issue/35/ - maker.variants = {''} - - # This is required because otherwise distlib creates scripts that are not - # executable. - # See https://bitbucket.org/pypa/distlib/issue/32/ - maker.set_mode = True + Given the mapping from entrypoint name to callable, return the relevant + console script specs. + """ + # Don't mutate caller's version + console = console.copy() scripts_to_generate = [] @@ -539,28 +407,364 @@ def install_unpacked_wheel( for k in easy_install_ep: del console[k] - # Generate the console and GUI entry points specified in the wheel + # Generate the console entry points specified in the wheel scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) + return scripts_to_generate + + +class ZipBackedFile(object): + def __init__(self, src_record_path, dest_path, zip_file): + # type: (RecordPath, text_type, ZipFile) -> None + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file + self.changed = False + + def _getinfo(self): + # type: () -> ZipInfo + if not PY2: + return self._zip_file.getinfo(self.src_record_path) + # Python 2 does not expose a way to detect a ZIP's encoding, but the + # wheel specification (PEP 427) explicitly mandates that paths should + # use UTF-8, so we assume it is true. + return self._zip_file.getinfo(self.src_record_path.encode("utf-8")) + + def save(self): + # type: () -> None + # directory creation is lazy and after file filtering + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + parent_dir = os.path.dirname(self.dest_path) + ensure_dir(parent_dir) + + # When we open the output file below, any existing file is truncated + # before we start writing the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(self.dest_path): + os.unlink(self.dest_path) + + zipinfo = self._getinfo() + + with self._zip_file.open(zipinfo) as f: + with open(self.dest_path, "wb") as dest: + shutil.copyfileobj(f, dest) + + if zip_item_is_executable(zipinfo): + set_extracted_file_to_default_mode_plus_executable(self.dest_path) + + +class ScriptFile(object): + def __init__(self, file): + # type: (File) -> None + self._file = file + self.src_record_path = self._file.src_record_path + self.dest_path = self._file.dest_path + self.changed = False + + def save(self): + # type: () -> None + self._file.save() + self.changed = fix_script(self.dest_path) + + +class MissingCallableSuffix(InstallationError): + def __init__(self, entry_point): + # type: (str) -> None + super(MissingCallableSuffix, self).__init__( + "Invalid script entry point: {} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information.".format(entry_point) + ) + + +def _raise_for_invalid_entrypoint(specification): + # type: (str) -> None + entry = get_export_entry(specification) + if entry is not None and entry.suffix is None: + raise MissingCallableSuffix(str(entry)) + + +class PipScriptMaker(ScriptMaker): + def make(self, specification, options=None): + # type: (str, Dict[str, Any]) -> List[str] + _raise_for_invalid_entrypoint(specification) + return super(PipScriptMaker, self).make(specification, options) + + +def _install_wheel( + name, # type: str + wheel_zip, # type: ZipFile + wheel_path, # type: str + scheme, # type: Scheme + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool +): + # type: (...) -> None + """Install a wheel. + + :param name: Name of the project to install + :param wheel_zip: open ZipFile for wheel being installed + :param scheme: Distutils scheme dictating the install directories + :param req_description: String used in place of the requirement, for + logging + :param pycompile: Whether to byte-compile installed Python files + :param warn_script_location: Whether to check that scripts are installed + into a directory on PATH + :raises UnsupportedWheel: + * when the directory holds an unpacked wheel with incompatible + Wheel-Version + * when the .dist-info dir does not match the wheel + """ + info_dir, metadata = parse_wheel(wheel_zip, name) + + if wheel_root_is_purelib(metadata): + lib_dir = scheme.purelib + else: + lib_dir = scheme.platlib + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} # type: Dict[RecordPath, RecordPath] + changed = set() # type: Set[RecordPath] + generated = [] # type: List[str] + + def record_installed(srcfile, destfile, modified=False): + # type: (RecordPath, text_type, bool) -> None + """Map archive RECORD paths to installation RECORD paths.""" + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath + if modified: + changed.add(_fs_to_record_path(destfile)) + + def all_paths(): + # type: () -> Iterable[RecordPath] + names = wheel_zip.namelist() + # If a flag is set, names may be unicode in Python 2. We convert to + # text explicitly so these are valid for lookup in RECORD. + decoded_names = map(ensure_text, names) + for name in decoded_names: + yield cast("RecordPath", name) + + def is_dir_path(path): + # type: (RecordPath) -> bool + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path, target_path): + # type: (text_type, text_type) -> None + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" + " outside the target directory {!r}" + ) + raise InstallationError( + message.format(wheel_path, target_path, dest_dir_path) + ) + + def root_scheme_file_maker(zip_file, dest): + # type: (ZipFile, text_type) -> Callable[[RecordPath], File] + def make_root_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + dest_path = os.path.join(dest, normed_path) + assert_no_path_traversal(dest, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_root_scheme_file + + def data_scheme_file_maker(zip_file, scheme): + # type: (ZipFile, Scheme) -> Callable[[RecordPath], File] + scheme_paths = {} + for key in SCHEME_KEYS: + encoded_key = ensure_text(key) + scheme_paths[encoded_key] = ensure_text( + getattr(scheme, key), encoding=sys.getfilesystemencoding() + ) + + def make_data_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + try: + _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) + except ValueError: + message = ( + "Unexpected file in {}: {!r}. .data directory contents" + " should be named like: '/'." + ).format(wheel_path, record_path) + raise InstallationError(message) + + try: + scheme_path = scheme_paths[scheme_key] + except KeyError: + valid_scheme_keys = ", ".join(sorted(scheme_paths)) + message = ( + "Unknown scheme key used in {}: {} (for file {!r}). .data" + " directory contents should be in subdirectories named" + " with a valid scheme key ({})" + ).format( + wheel_path, scheme_key, record_path, valid_scheme_keys + ) + raise InstallationError(message) + + dest_path = os.path.join(scheme_path, dest_subpath) + assert_no_path_traversal(scheme_path, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_data_scheme_file + + def is_data_scheme_path(path): + # type: (RecordPath) -> bool + return path.split("/", 1)[0].endswith(".data") + + paths = all_paths() + file_paths = filterfalse(is_dir_path, paths) + root_scheme_paths, data_scheme_paths = partition( + is_data_scheme_path, file_paths + ) + + make_root_scheme_file = root_scheme_file_maker( + wheel_zip, + ensure_text(lib_dir, encoding=sys.getfilesystemencoding()), + ) + files = map(make_root_scheme_file, root_scheme_paths) + + def is_script_scheme_path(path): + # type: (RecordPath) -> bool + parts = path.split("/", 2) + return ( + len(parts) > 2 and + parts[0].endswith(".data") and + parts[1] == "scripts" + ) + + other_scheme_paths, script_scheme_paths = partition( + is_script_scheme_path, data_scheme_paths + ) + + make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) + other_scheme_files = map(make_data_scheme_file, other_scheme_paths) + files = chain(files, other_scheme_files) + + # Get the defined entry points + distribution = pkg_resources_distribution_for_wheel( + wheel_zip, name, wheel_path + ) + console, gui = get_entrypoints(distribution) + + def is_entrypoint_wrapper(file): + # type: (File) -> bool + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + path = file.dest_path + name = os.path.basename(path) + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + script_scheme_files = map(make_data_scheme_file, script_scheme_paths) + script_scheme_files = filterfalse( + is_entrypoint_wrapper, script_scheme_files + ) + script_scheme_files = map(ScriptFile, script_scheme_files) + files = chain(files, script_scheme_files) + + for file in files: + file.save() + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths(): + # type: () -> Iterator[text_type] + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug + # issues related to permissions on existing files. + for installed_path in sorted(set(installed.values())): + full_installed_path = os.path.join(lib_dir, installed_path) + if not os.path.isfile(full_installed_path): + continue + if not full_installed_path.endswith('.py'): + continue + yield full_installed_path + + def pyc_output_path(path): + # type: (text_type) -> text_type + """Return the path the pyc file would have been written to. + """ + if PY2: + if sys.flags.optimize: + return path + 'o' + else: + return path + 'c' + else: + return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + for path in pyc_source_file_paths(): + # Python 2's `compileall.compile_file` requires a str in + # error cases, so we must convert to the native type. + path_arg = ensure_str( + path, encoding=sys.getfilesystemencoding() + ) + success = compileall.compile_file( + path_arg, force=True, quiet=True + ) + if success: + pyc_path = pyc_output_path(path) + assert os.path.exists(pyc_path) + pyc_record_path = cast( + "RecordPath", pyc_path.replace(os.path.sep, "/") + ) + record_installed(pyc_record_path, pyc_path) + logger.debug(stdout.getvalue()) + + maker = PipScriptMaker(None, scheme.scripts) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {''} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Generate the console and GUI entry points specified in the wheel + scripts_to_generate = get_console_script_specs(console) + gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) - generated_console_scripts = [] # type: List[str] + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) - try: - generated_console_scripts = maker.make_multiple(scripts_to_generate) - generated.extend(generated_console_scripts) - - generated.extend( - maker.make_multiple(gui_scripts_to_generate, {'gui': True}) - ) - except MissingCallableSuffix as e: - entry = e.args[0] - raise InstallationError( - "Invalid script entry point: {} for req: {} - A callable " - "suffix is required. Cf https://packaging.python.org/" - "specifications/entry-points/#use-for-scripts for more " - "information.".format(entry, req_description) - ) + generated.extend( + maker.make_multiple(gui_scripts_to_generate, {'gui': True}) + ) if warn_script_location: msg = message_about_scripts_not_on_PATH(generated_console_scripts) @@ -577,6 +781,8 @@ def install_unpacked_wheel( os.chmod(f.name, generated_file_mode) replace(f.name, path) + dest_info_dir = os.path.join(lib_dir, info_dir) + # Record pip as the installer installer_path = os.path.join(dest_info_dir, 'INSTALLER') with _generate_file(installer_path) as installer_file: @@ -590,18 +796,44 @@ def install_unpacked_wheel( direct_url_file.write(direct_url.to_json().encode("utf-8")) generated.append(direct_url_path) + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, 'REQUESTED') + with open(requested_path, "w"): + pass + generated.append(requested_path) + + record_text = distribution.get_metadata('RECORD') + record_rows = list(csv.reader(record_text.splitlines())) + + rows = get_csv_rows_for_installed( + record_rows, + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir) + # Record details of all files installed record_path = os.path.join(dest_info_dir, 'RECORD') - with open(record_path, **csv_io_kwargs('r')) as record_file: - rows = get_csv_rows_for_installed( - csv.reader(record_file), - installed=installed, - changed=changed, - generated=generated, - lib_dir=lib_dir) + with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: - writer = csv.writer(record_file) - writer.writerows(sorted_outrows(rows)) # sort to simplify testing + # The type mypy infers for record_file is different for Python 3 + # (typing.IO[Any]) and Python 2 (typing.BinaryIO). We explicitly + # cast to typing.IO[str] as a workaround. + writer = csv.writer(cast('IO[str]', record_file)) + writer.writerows(_normalized_outrows(rows)) + + +@contextlib.contextmanager +def req_error_context(req_description): + # type: (str) -> Iterator[None] + try: + yield + except InstallationError as e: + message = "For req: {}. {}".format(req_description, e.args[0]) + reraise( + InstallationError, InstallationError(message), sys.exc_info()[2] + ) def install_wheel( @@ -611,21 +843,19 @@ def install_wheel( req_description, # type: str pycompile=True, # type: bool warn_script_location=True, # type: bool - _temp_dir_for_testing=None, # type: Optional[str] direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool ): # type: (...) -> None - with TempDirectory( - path=_temp_dir_for_testing, kind="unpacked-wheel" - ) as unpacked_dir, ZipFile(wheel_path, allowZip64=True) as z: - unpack_file(wheel_path, unpacked_dir.path) - install_unpacked_wheel( - name=name, - wheeldir=unpacked_dir.path, - wheel_zip=z, - scheme=scheme, - req_description=req_description, - pycompile=pycompile, - warn_script_location=warn_script_location, - direct_url=direct_url, - ) + with ZipFile(wheel_path, allowZip64=True) as z: + with req_error_context(req_description): + _install_wheel( + name=name, + wheel_zip=z, + wheel_path=wheel_path, + scheme=scheme, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=requested, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py index 1fcbb77..a5455fc 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py @@ -9,7 +9,6 @@ import mimetypes import os import shutil -from pip._vendor import requests from pip._vendor.six import PY2 from pip._internal.distributions import ( @@ -21,6 +20,7 @@ from pip._internal.exceptions import ( HashMismatch, HashUnpinned, InstallationError, + NetworkConnectionError, PreviousBuildDirError, VcsHashUnsupported, ) @@ -260,8 +260,9 @@ def unpack_url( ) # unpack the archive to the build dir location. even when only downloading - # archives, they have to be unpacked to parse dependencies - unpack_file(file.path, location, file.content_type) + # archives, they have to be unpacked to parse dependencies, except wheels + if not link.is_wheel: + unpack_file(file.path, location, file.content_type) return file @@ -376,104 +377,98 @@ class RequirementPreparer(object): "Could not find or access download directory '{}'" .format(self.download_dir)) - def prepare_linked_requirement( - self, - req, # type: InstallRequirement - ): - # type: (...) -> AbstractDistribution - """Prepare a requirement that would be obtained from req.link - """ - assert req.link - link = req.link - - # TODO: Breakup into smaller functions - if link.scheme == 'file': - path = link.file_path + def _log_preparing_link(self, req): + # type: (InstallRequirement) -> None + """Log the way the link prepared.""" + if req.link.is_file: + path = req.link.file_path logger.info('Processing %s', display_path(path)) else: logger.info('Collecting %s', req.req or req) - download_dir = self.download_dir - if link.is_wheel and self.wheel_download_dir: - # when doing 'pip wheel` we download wheels to a - # dedicated dir. - download_dir = self.wheel_download_dir + def _ensure_link_req_src_dir(self, req, download_dir, parallel_builds): + # type: (InstallRequirement, Optional[str], bool) -> None + """Ensure source_dir of a linked InstallRequirement.""" + # Since source_dir is only set for editable requirements. + if req.link.is_wheel: + # We don't need to unpack wheels, so no need for a source + # directory. + return + assert req.source_dir is None + # We always delete unpacked sdists after pip runs. + req.ensure_has_source_dir( + self.build_dir, + autodelete=True, + parallel_builds=parallel_builds, + ) - if link.is_wheel: - if download_dir: - # When downloading, we only unpack wheels to get - # metadata. - autodelete_unpacked = True - else: - # When installing a wheel, we use the unpacked - # wheel. - autodelete_unpacked = False + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '{}' due to a" + "pre-existing build directory ({}). This is likely " + "due to a previous installation that failed . pip is " + "being responsible and not assuming it can delete this. " + "Please delete it and try again.".format(req, req.source_dir) + ) + + def _get_linked_req_hashes(self, req): + # type: (InstallRequirement) -> Hashes + # By the time this is called, the requirement's link should have + # been checked so we can tell what kind of requirements req is + # and raise some more informative errors than otherwise. + # (For example, we can raise VcsHashUnsupported for a VCS URL + # rather than HashMissing.) + if not self.require_hashes: + return req.hashes(trust_internet=True) + + # We could check these first 2 conditions inside unpack_url + # and save repetition of conditions, but then we would + # report less-useful error messages for unhashable + # requirements, complaining that there's no hash provided. + if req.link.is_vcs: + raise VcsHashUnsupported() + if req.link.is_existing_dir(): + raise DirectoryUrlHashUnsupported() + + # Unpinned packages are asking for trouble when a new version + # is uploaded. This isn't a security check, but it saves users + # a surprising hash mismatch in the future. + # file:/// URLs aren't pinnable, so don't complain about them + # not being pinned. + if req.original_link is None and not req.is_pinned: + raise HashUnpinned() + + # If known-good hashes are missing for this requirement, + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + return req.hashes(trust_internet=False) or MissingHashes() + + def prepare_linked_requirement(self, req, parallel_builds=False): + # type: (InstallRequirement, bool) -> AbstractDistribution + """Prepare a requirement to be obtained from req.link.""" + assert req.link + link = req.link + self._log_preparing_link(req) + if link.is_wheel and self.wheel_download_dir: + # Download wheels to a dedicated dir when doing `pip wheel`. + download_dir = self.wheel_download_dir else: - # We always delete unpacked sdists after pip runs. - autodelete_unpacked = True + download_dir = self.download_dir with indent_log(): - # Since source_dir is only set for editable requirements. - assert req.source_dir is None - req.ensure_has_source_dir(self.build_dir, autodelete_unpacked) - # If a checkout exists, it's unwise to keep going. version - # inconsistencies are logged later, but do not fail the - # installation. - # FIXME: this won't upgrade when there's an existing - # package unpacked in `req.source_dir` - if os.path.exists(os.path.join(req.source_dir, 'setup.py')): - raise PreviousBuildDirError( - "pip can't proceed with requirements '{}' due to a" - " pre-existing build directory ({}). This is " - "likely due to a previous installation that failed" - ". pip is being responsible and not assuming it " - "can delete this. Please delete it and try again." - .format(req, req.source_dir) - ) - - # Now that we have the real link, we can tell what kind of - # requirements we have and raise some more informative errors - # than otherwise. (For example, we can raise VcsHashUnsupported - # for a VCS URL rather than HashMissing.) - if self.require_hashes: - # We could check these first 2 conditions inside - # unpack_url and save repetition of conditions, but then - # we would report less-useful error messages for - # unhashable requirements, complaining that there's no - # hash provided. - if link.is_vcs: - raise VcsHashUnsupported() - elif link.is_existing_dir(): - raise DirectoryUrlHashUnsupported() - if not req.original_link and not req.is_pinned: - # Unpinned packages are asking for trouble when a new - # version is uploaded. This isn't a security check, but - # it saves users a surprising hash mismatch in the - # future. - # - # file:/// URLs aren't pinnable, so don't complain - # about them not being pinned. - raise HashUnpinned() - - hashes = req.hashes(trust_internet=not self.require_hashes) - if self.require_hashes and not hashes: - # Known-good hashes are missing for this requirement, so - # shim it with a facade object that will provoke hash - # computation and then raise a HashMissing exception - # showing the user what the hash should be. - hashes = MissingHashes() - + self._ensure_link_req_src_dir(req, download_dir, parallel_builds) try: local_file = unpack_url( link, req.source_dir, self.downloader, download_dir, - hashes=hashes, - ) - except requests.HTTPError as exc: - logger.critical( - 'Could not install requirement %s because of error %s', - req, - exc, + hashes=self._get_linked_req_hashes(req) ) + except NetworkConnectionError as exc: raise InstallationError( 'Could not install requirement {} because of HTTP ' 'error {} for URL {}'.format(req, exc, link) @@ -497,9 +492,8 @@ class RequirementPreparer(object): ) if not os.path.exists(download_location): shutil.copy(local_file.path, download_location) - logger.info( - 'Saved %s', display_path(download_location) - ) + download_path = display_path(download_location) + logger.info('Saved %s', download_path) if self._download_should_save: # Make a .zip of the source_dir we already created. diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py index d2d027a..8568d3f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py @@ -1,8 +1,6 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import +import collections import logging from pip._internal.utils.logging import indent_log @@ -13,7 +11,7 @@ from .req_install import InstallRequirement from .req_set import RequirementSet if MYPY_CHECK_RUNNING: - from typing import Any, List, Sequence + from typing import Iterator, List, Optional, Sequence, Tuple __all__ = [ "RequirementSet", "InstallRequirement", @@ -33,12 +31,25 @@ class InstallationResult(object): return "InstallationResult(name={!r})".format(self.name) +def _validate_requirements( + requirements, # type: List[InstallRequirement] +): + # type: (...) -> Iterator[Tuple[str, InstallRequirement]] + for req in requirements: + assert req.name, "invalid to-be-installed requirement: {}".format(req) + yield req.name, req + + def install_given_reqs( - to_install, # type: List[InstallRequirement] + requirements, # type: List[InstallRequirement] install_options, # type: List[str] - global_options=(), # type: Sequence[str] - *args, # type: Any - **kwargs # type: Any + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + warn_script_location, # type: bool + use_user_site, # type: bool + pycompile, # type: bool ): # type: (...) -> List[InstallationResult] """ @@ -46,47 +57,47 @@ def install_given_reqs( (to be called after having downloaded and unpacked the packages) """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) if to_install: logger.info( 'Installing collected packages: %s', - ', '.join([req.name for req in to_install]), + ', '.join(to_install.keys()), ) installed = [] with indent_log(): - for requirement in to_install: + for req_name, requirement in to_install.items(): if requirement.should_reinstall: - logger.info('Attempting uninstall: %s', requirement.name) + logger.info('Attempting uninstall: %s', req_name) with indent_log(): uninstalled_pathset = requirement.uninstall( auto_confirm=True ) + else: + uninstalled_pathset = None + try: requirement.install( install_options, global_options, - *args, - **kwargs + root=root, + home=home, + prefix=prefix, + warn_script_location=warn_script_location, + use_user_site=use_user_site, + pycompile=pycompile, ) except Exception: - should_rollback = ( - requirement.should_reinstall and - not requirement.install_succeeded - ) # if install did not succeed, rollback previous uninstall - if should_rollback: + if uninstalled_pathset and not requirement.install_succeeded: uninstalled_pathset.rollback() raise else: - should_commit = ( - requirement.should_reinstall and - requirement.install_succeeded - ) - if should_commit: + if uninstalled_pathset and requirement.install_succeeded: uninstalled_pathset.commit() - installed.append(InstallationResult(requirement.name)) + installed.append(InstallationResult(req_name)) return installed diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py b/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py index c9f1fe7..7a4641e 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py @@ -8,9 +8,6 @@ These are meant to be used elsewhere within pip to create instances of InstallRequirement. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import logging import os import re @@ -26,6 +23,7 @@ from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel from pip._internal.pyproject import make_pyproject_path from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS from pip._internal.utils.misc import is_installable_dir, splitext from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -78,7 +76,7 @@ def convert_extras(extras): def parse_editable(editable_req): - # type: (str) -> Tuple[Optional[str], str, Optional[Set[str]]] + # type: (str) -> Tuple[Optional[str], str, Set[str]] """Parses an editable requirement into: - a requirement name - an URL @@ -120,7 +118,7 @@ def parse_editable(editable_req): Requirement("placeholder" + extras.lower()).extras, ) else: - return package_name, url_no_extras, None + return package_name, url_no_extras, set() for version_control in vcs: if url.lower().startswith('{}:'.format(version_control)): @@ -149,7 +147,7 @@ def parse_editable(editable_req): "Could not detect requirement name for '{}', please specify one " "with #egg=your_package_name".format(editable_req) ) - return package_name, url, None + return package_name, url, set() def deduce_helpful_msg(req): @@ -175,8 +173,9 @@ def deduce_helpful_msg(req): " the packages specified within it." ).format(req) except RequirementParseError: - logger.debug("Cannot parse '{}' as requirements \ - file".format(req), exc_info=True) + logger.debug( + "Cannot parse '%s' as requirements file", req, exc_info=True + ) else: msg += " File '{}' does not exist.".format(req) return msg @@ -222,7 +221,8 @@ def install_req_from_editable( use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] - constraint=False # type: bool + constraint=False, # type: bool + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement @@ -231,6 +231,7 @@ def install_req_from_editable( return InstallRequirement( parts.requirement, comes_from=comes_from, + user_supplied=user_supplied, editable=True, link=parts.link, constraint=constraint, @@ -264,7 +265,7 @@ def _looks_like_path(name): def _get_url_from_path(path, name): - # type: (str, str) -> str + # type: (str, str) -> Optional[str] """ First, it checks whether a provided path is an installable directory (e.g. it has a setup.py). If it is, returns the path. @@ -371,6 +372,17 @@ def parse_req_from_line(name, line_source): if add_msg: msg += '\nHint: {}'.format(add_msg) raise InstallationError(msg) + else: + # Deprecate extras after specifiers: "name>=1.0[extras]" + # This currently works by accident because _strip_extras() parses + # any extras in the end of the string and those are saved in + # RequirementParts + for spec in req.specifier: + spec_str = str(spec) + if spec_str.endswith(']'): + msg = "Extras after version '{}'.".format(spec_str) + replace = "moving the extras before version specifiers" + deprecated(msg, replacement=replace, gone_in="21.0") else: req = None @@ -385,6 +397,7 @@ def install_req_from_line( options=None, # type: Optional[Dict[str, Any]] constraint=False, # type: bool line_source=None, # type: Optional[str] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement """Creates an InstallRequirement from a name, which might be a @@ -403,6 +416,7 @@ def install_req_from_line( hash_options=options.get("hashes", {}) if options else {}, constraint=constraint, extras=parts.extras, + user_supplied=user_supplied, ) @@ -410,7 +424,8 @@ def install_req_from_req_string( req_string, # type: str comes_from=None, # type: Optional[InstallRequirement] isolated=False, # type: bool - use_pep517=None # type: Optional[bool] + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement try: @@ -432,14 +447,19 @@ def install_req_from_req_string( ) return InstallRequirement( - req, comes_from, isolated=isolated, use_pep517=use_pep517 + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, ) def install_req_from_parsed_requirement( parsed_req, # type: ParsedRequirement isolated=False, # type: bool - use_pep517=None # type: Optional[bool] + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement if parsed_req.is_editable: @@ -449,6 +469,7 @@ def install_req_from_parsed_requirement( use_pep517=use_pep517, constraint=parsed_req.constraint, isolated=isolated, + user_supplied=user_supplied, ) else: @@ -460,5 +481,6 @@ def install_req_from_parsed_requirement( options=parsed_req.options, constraint=parsed_req.constraint, line_source=parsed_req.line_source, + user_supplied=user_supplied, ) return req diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py index 63cab76..1050582 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py @@ -2,9 +2,6 @@ Requirements file parsing """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import import optparse @@ -21,6 +18,7 @@ from pip._internal.exceptions import ( RequirementsFileParseError, ) from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status from pip._internal.utils.encoding import auto_decode from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import get_url_scheme @@ -60,10 +58,11 @@ SUPPORTED_OPTIONS = [ cmdoptions.find_links, cmdoptions.no_binary, cmdoptions.only_binary, + cmdoptions.prefer_binary, cmdoptions.require_hashes, cmdoptions.pre, cmdoptions.trusted_host, - cmdoptions.always_unzip, # Deprecated + cmdoptions.use_new_feature, ] # type: List[Callable[..., optparse.Option]] # options to be passed to requirements @@ -101,7 +100,7 @@ class ParsedLine(object): self, filename, # type: str lineno, # type: int - comes_from, # type: str + comes_from, # type: Optional[str] args, # type: str opts, # type: Values constraint, # type: bool @@ -135,7 +134,7 @@ def parse_requirements( constraint=False, # type: bool ): # type: (...) -> Iterator[ParsedRequirement] - """Parse a requirements file and yield InstallRequirement instances. + """Parse a requirements file and yield ParsedRequirement instances. :param filename: Path or url of requirements file. :param session: PipSession instance. @@ -226,12 +225,18 @@ def handle_option_line( ): # type: (...) -> None - # percolate hash-checking option upward - if opts.require_hashes: - options.require_hashes = opts.require_hashes + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled + if f not in options.features_enabled + ) # set finder options - elif finder: + if finder: find_links = finder.find_links index_urls = finder.index_urls if opts.index_url: @@ -260,6 +265,9 @@ def handle_option_line( if opts.pre: finder.set_allow_all_prereleases() + if opts.prefer_binary: + finder.set_prefer_binary() + if session: for host in opts.trusted_hosts or []: source = 'line {} of {}'.format(lineno, filename) @@ -316,7 +324,7 @@ class RequirementsFileParser(object): self, session, # type: PipSession line_parser, # type: LineParser - comes_from, # type: str + comes_from, # type: Optional[str] ): # type: (...) -> None self._session = session @@ -478,6 +486,7 @@ def join_lines(lines_enum): line = ' ' + line if new_line: new_line.append(line) + assert primary_line_number is not None yield primary_line_number, ''.join(new_line) new_line = [] else: @@ -489,6 +498,7 @@ def join_lines(lines_enum): # last line contains \ if new_line: + assert primary_line_number is not None yield primary_line_number, ''.join(new_line) # TODO: handle space after '\'. @@ -549,7 +559,7 @@ def get_file_content(url, session, comes_from=None): if scheme in ['http', 'https']: # FIXME: catch some errors resp = session.get(url) - resp.raise_for_status() + raise_for_status(resp) return resp.url, resp.text elif scheme == 'file': diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py index 3b28209..4759f4a 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py @@ -7,6 +7,7 @@ import logging import os import shutil import sys +import uuid import zipfile from pip._vendor import pkg_resources, six @@ -40,6 +41,7 @@ from pip._internal.utils.misc import ( display_path, dist_in_site_packages, dist_in_usersite, + get_distribution, get_installed_version, hide_url, redact_auth_from_url, @@ -110,7 +112,8 @@ class InstallRequirement(object): global_options=None, # type: Optional[List[str]] hash_options=None, # type: Optional[Dict[str, List[str]]] constraint=False, # type: bool - extras=() # type: Iterable[str] + extras=(), # type: Iterable[str] + user_supplied=False, # type: bool ): # type: (...) -> None assert req is None or isinstance(req, Requirement), req @@ -171,7 +174,10 @@ class InstallRequirement(object): self.hash_options = hash_options if hash_options else {} # Set to True after successful preparation of this requirement self.prepared = False - self.is_direct = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = user_supplied # Set by the legacy resolver when the requirement has been downloaded # TODO: This introduces a strong coupling between the resolver and the @@ -339,8 +345,8 @@ class InstallRequirement(object): s += '->' + comes_from return s - def ensure_build_location(self, build_dir, autodelete): - # type: (str, bool) -> str + def ensure_build_location(self, build_dir, autodelete, parallel_builds): + # type: (str, bool, bool) -> str assert build_dir is not None if self._temp_build_dir is not None: assert self._temp_build_dir.path @@ -354,16 +360,19 @@ class InstallRequirement(object): ) return self._temp_build_dir.path - if self.editable: - name = self.name.lower() - else: - name = self.name + + # When parallel builds are enabled, add a UUID to the build directory + # name so multiple builds do not interfere with each other. + dir_name = canonicalize_name(self.name) + if parallel_builds: + dir_name = "{}_{}".format(dir_name, uuid.uuid4().hex) + # FIXME: Is there a better place to create the build_dir? (hg and bzr # need this) if not os.path.exists(build_dir): logger.debug('Creating directory %s', build_dir) os.makedirs(build_dir) - actual_build_dir = os.path.join(build_dir, name) + actual_build_dir = os.path.join(build_dir, dir_name) # `None` indicates that we respect the globally-configured deletion # settings, which is what we actually want when auto-deleting. delete_arg = None if autodelete else False @@ -420,20 +429,13 @@ class InstallRequirement(object): """ if self.req is None: return - # get_distribution() will resolve the entire list of requirements - # anyway, and we've already determined that we need the requirement - # in question, so strip the marker so that we don't try to - # evaluate it. - no_marker = Requirement(str(self.req)) - no_marker.marker = None - try: - self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) - except pkg_resources.DistributionNotFound: + existing_dist = get_distribution(self.req.name) + if not existing_dist: return - except pkg_resources.VersionConflict: - existing_dist = pkg_resources.get_distribution( - self.req.name - ) + + existing_version = existing_dist.parsed_version + if not self.req.specifier.contains(existing_version, prereleases=True): + self.satisfied_by = None if use_user_site: if dist_in_usersite(existing_dist): self.should_reinstall = True @@ -447,11 +449,13 @@ class InstallRequirement(object): else: self.should_reinstall = True else: - if self.editable and self.satisfied_by: + if self.editable: self.should_reinstall = True # when installing editables, nothing pre-existing should ever # satisfy self.satisfied_by = None + else: + self.satisfied_by = existing_dist # Things valid for wheels @property @@ -588,8 +592,13 @@ class InstallRequirement(object): ) # For both source distributions and editables - def ensure_has_source_dir(self, parent_dir, autodelete=False): - # type: (str, bool) -> None + def ensure_has_source_dir( + self, + parent_dir, + autodelete=False, + parallel_builds=False, + ): + # type: (str, bool, bool) -> None """Ensure that a source_dir is set. This will create a temporary build dir if the name of the requirement @@ -601,7 +610,9 @@ class InstallRequirement(object): """ if self.source_dir is None: self.source_dir = self.ensure_build_location( - parent_dir, autodelete + parent_dir, + autodelete=autodelete, + parallel_builds=parallel_builds, ) # For editable installations @@ -664,13 +675,11 @@ class InstallRequirement(object): """ assert self.req - try: - dist = pkg_resources.get_distribution(self.req.name) - except pkg_resources.DistributionNotFound: + dist = get_distribution(self.req.name) + if not dist: logger.warning("Skipping %s as it is not installed.", self.name) return None - else: - logger.info('Found existing installation: %s', dist) + logger.info('Found existing installation: %s', dist) uninstalled_pathset = UninstallPathSet.from_dist(dist) uninstalled_pathset.remove(auto_confirm, verbose) @@ -809,6 +818,7 @@ class InstallRequirement(object): pycompile=pycompile, warn_script_location=warn_script_location, direct_url=direct_url, + requested=self.user_supplied, ) self.install_succeeded = True return @@ -848,3 +858,35 @@ class InstallRequirement(object): raise self.install_succeeded = success + + +def check_invalid_constraint_type(req): + # type: (InstallRequirement) -> str + + # Check for unsupported forms + problem = "" + if not req.name: + problem = "Unnamed requirements are not allowed as constraints" + elif req.link: + problem = "Links are not allowed as constraints" + elif req.extras: + problem = "Constraints cannot have extras" + + if problem: + deprecated( + reason=( + "Constraints are only allowed to take the form of a package " + "name and a version specifier. Other forms were originally " + "permitted as an accident of the implementation, but were " + "undocumented. The new implementation of the resolver no " + "longer supports these forms." + ), + replacement=( + "replacing the constraint with a requirement." + ), + # No plan yet for when the new resolver becomes default + gone_in=None, + issue=8210 + ) + + return problem diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py index f168ce1..ab4b6f8 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import import logging @@ -110,9 +107,8 @@ class RequirementSet(object): ) # This next bit is really a sanity check. - assert install_req.is_direct == (parent_req_name is None), ( - "a direct req shouldn't have a parent and also, " - "a non direct req should have a parent" + assert not install_req.user_supplied or parent_req_name is None, ( + "a user supplied req shouldn't have a parent" ) # Unnamed requirements are scanned again and the requirement won't be @@ -122,7 +118,8 @@ class RequirementSet(object): return [install_req], None try: - existing_req = self.get_requirement(install_req.name) + existing_req = self.get_requirement( + install_req.name) # type: Optional[InstallRequirement] except KeyError: existing_req = None @@ -167,6 +164,10 @@ class RequirementSet(object): # If we're now installing a constraint, mark the existing # object for real installation. existing_req.constraint = False + # If we're now installing a user supplied requirement, + # mark the existing object as such. + if install_req.user_supplied: + existing_req.user_supplied = True existing_req.extras = tuple(sorted( set(existing_req.extras) | set(install_req.extras) )) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py index 14adeab..13fb245 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import import contextlib @@ -98,6 +95,7 @@ class RequirementTracker(object): """Add an InstallRequirement to build tracking. """ + assert req.link # Get the file to write information about this requirement. entry_path = self._entry_path(req.link) @@ -130,6 +128,7 @@ class RequirementTracker(object): """Remove an InstallRequirement from build tracking. """ + assert req.link # Delete the created file and the corresponding entries. os.unlink(self._entry_path(req.link)) self._entries.remove(req) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py index 559061a..69719d3 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py @@ -612,8 +612,7 @@ class UninstallPthEntries(object): # If the file doesn't exist, log a warning and return if not os.path.isfile(self.file): logger.warning( - "Cannot remove entries from nonexistent file {}".format( - self.file) + "Cannot remove entries from nonexistent file %s", self.file ) return with open(self.file, 'rb') as fh: diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py index cdb44d1..c9b4c66 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -28,6 +28,7 @@ from pip._internal.exceptions import ( HashErrors, UnsupportedPythonVersion, ) +from pip._internal.req.req_install import check_invalid_constraint_type from pip._internal.req.req_set import RequirementSet from pip._internal.resolution.base import BaseResolver from pip._internal.utils.compatibility_tags import get_supported @@ -46,6 +47,7 @@ if MYPY_CHECK_RUNNING: from pip._internal.cache import WheelCache from pip._internal.distributions import AbstractDistribution from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link from pip._internal.operations.prepare import RequirementPreparer from pip._internal.req.req_install import InstallRequirement from pip._internal.resolution.base import InstallRequirementProvider @@ -166,6 +168,8 @@ class Resolver(BaseResolver): check_supported_wheels=check_supported_wheels ) for req in root_reqs: + if req.constraint: + check_invalid_constraint_type(req) requirement_set.add_requirement(req) # Actually prepare the files, and collect any exceptions. Most hash @@ -174,7 +178,7 @@ class Resolver(BaseResolver): # based on link type. discovered_reqs = [] # type: List[InstallRequirement] hash_errors = HashErrors() - for req in chain(root_reqs, discovered_reqs): + for req in chain(requirement_set.all_requirements, discovered_reqs): try: discovered_reqs.extend(self._resolve_one(requirement_set, req)) except HashError as exc: @@ -194,7 +198,7 @@ class Resolver(BaseResolver): return True else: assert self.upgrade_strategy == "only-if-needed" - return req.is_direct + return req.user_supplied or req.constraint def _set_req_to_reinstall(self, req): # type: (InstallRequirement) -> None @@ -260,6 +264,29 @@ class Resolver(BaseResolver): self._set_req_to_reinstall(req_to_install) return None + def _find_requirement_link(self, req): + # type: (InstallRequirement) -> Optional[Link] + upgrade = self._is_upgrade_allowed(req) + best_candidate = self.finder.find_requirement(req, upgrade) + if not best_candidate: + return None + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or '' + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + u'The candidate selected for download or install is a ' + 'yanked version: {candidate}\n' + 'Reason for being yanked: {reason}' + ).format(candidate=best_candidate, reason=reason) + logger.warning(msg) + + return link + def _populate_link(self, req): # type: (InstallRequirement) -> None """Ensure that if a link can be found for this, that it is found. @@ -274,9 +301,8 @@ class Resolver(BaseResolver): mismatches. Furthermore, cached wheels at present have undeterministic contents due to file modification times. """ - upgrade = self._is_upgrade_allowed(req) if req.link is None: - req.link = self.finder.find_requirement(req, upgrade) + req.link = self._find_requirement_link(req) if self.wheel_cache is None or self.preparer.require_hashes: return @@ -396,7 +422,7 @@ class Resolver(BaseResolver): # 'unnamed' requirements will get added here # 'unnamed' requirements can only come from being directly # provided by the user. - assert req_to_install.is_direct + assert req_to_install.user_supplied requirement_set.add_requirement( req_to_install, parent_req_name=None, ) @@ -412,7 +438,7 @@ class Resolver(BaseResolver): ) for missing in missing_requested: logger.warning( - '%s does not provide the extra \'%s\'', + "%s does not provide the extra '%s'", dist, missing ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py index 5f99618..9245747 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -3,14 +3,21 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Optional, Sequence, Set + from typing import FrozenSet, Iterable, Optional, Tuple - from pip._internal.req.req_install import InstallRequirement from pip._vendor.packaging.version import _BaseVersion + from pip._internal.models.link import Link + from pip._internal.req.req_install import InstallRequirement + + CandidateLookup = Tuple[ + Optional["Candidate"], + Optional[InstallRequirement], + ] + def format_name(project, extras): - # type: (str, Set[str]) -> str + # type: (str, FrozenSet[str]) -> str if not extras: return project canonical_extras = sorted(canonicalize_name(e) for e in extras) @@ -23,14 +30,18 @@ class Requirement(object): # type: () -> str raise NotImplementedError("Subclass should override") - def find_matches(self): - # type: () -> Sequence[Candidate] - raise NotImplementedError("Subclass should override") - def is_satisfied_by(self, candidate): # type: (Candidate) -> bool return False + def get_candidate_lookup(self): + # type: () -> CandidateLookup + raise NotImplementedError("Subclass should override") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") + class Candidate(object): @property @@ -43,10 +54,29 @@ class Candidate(object): # type: () -> _BaseVersion raise NotImplementedError("Override in subclass") - def get_dependencies(self): - # type: () -> Sequence[Requirement] + @property + def is_installed(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def is_editable(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def source_link(self): + # type: () -> Optional[Link] + raise NotImplementedError("Override in subclass") + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] raise NotImplementedError("Override in subclass") def get_install_requirement(self): # type: () -> Optional[InstallRequirement] raise NotImplementedError("Override in subclass") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py index f8461ad..46cc7e7 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -1,23 +1,30 @@ import logging import sys +from pip._vendor.contextlib2 import suppress from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.version import Version +from pip._internal.exceptions import HashError, MetadataInconsistent +from pip._internal.network.lazy_wheel import ( + HTTPRangeRequestUnsupported, + dist_from_wheel_url, +) from pip._internal.req.constructors import ( install_req_from_editable, install_req_from_line, ) from pip._internal.req.req_install import InstallRequirement -from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_is_editable, normalize_version_info from pip._internal.utils.packaging import get_requires_python from pip._internal.utils.typing import MYPY_CHECK_RUNNING from .base import Candidate, format_name if MYPY_CHECK_RUNNING: - from typing import Any, Optional, Sequence, Set, Tuple, Union + from typing import Any, FrozenSet, Iterable, Optional, Tuple, Union from pip._vendor.packaging.version import _BaseVersion from pip._vendor.pkg_resources import Distribution @@ -38,55 +45,69 @@ if MYPY_CHECK_RUNNING: logger = logging.getLogger(__name__) -def make_install_req_from_link(link, parent): +def make_install_req_from_link(link, template): # type: (Link, InstallRequirement) -> InstallRequirement - assert not parent.editable, "parent is editable" - return install_req_from_line( - link.url, - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, + assert not template.editable, "template is editable" + if template.req: + line = str(template.req) + else: + line = link.url + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options ), ) + ireq.original_link = template.original_link + ireq.link = link + return ireq -def make_install_req_from_editable(link, parent): +def make_install_req_from_editable(link, template): # type: (Link, InstallRequirement) -> InstallRequirement - assert parent.editable, "parent not editable" + assert template.editable, "template not editable" return install_req_from_editable( link.url, - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options ), ) -def make_install_req_from_dist(dist, parent): +def make_install_req_from_dist(dist, template): # type: (Distribution, InstallRequirement) -> InstallRequirement + project_name = canonicalize_name(dist.project_name) + if template.req: + line = str(template.req) + elif template.link: + line = "{} @ {}".format(project_name, template.link.url) + else: + line = "{}=={}".format(project_name, dist.parsed_version) ireq = install_req_from_line( - "{}=={}".format( - canonicalize_name(dist.project_name), - dist.parsed_version, - ), - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options ), ) ireq.satisfied_by = dist @@ -94,33 +115,56 @@ def make_install_req_from_dist(dist, parent): class _InstallRequirementBackedCandidate(Candidate): + """A candidate backed by an ``InstallRequirement``. + + This represents a package request with the target not being already + in the environment, and needs to be fetched and installed. The backing + ``InstallRequirement`` is responsible for most of the leg work; this + class exposes appropriate information to the resolver. + + :param link: The link passed to the ``InstallRequirement``. The backing + ``InstallRequirement`` will use this link to fetch the distribution. + :param source_link: The link this candidate "originates" from. This is + different from ``link`` when the link is found in the wheel cache. + ``link`` would point to the wheel cache, while this points to the + found remote link (e.g. from pypi.org). + """ + is_installed = False + def __init__( self, link, # type: Link + source_link, # type: Link ireq, # type: InstallRequirement factory, # type: Factory name=None, # type: Optional[str] version=None, # type: Optional[_BaseVersion] ): # type: (...) -> None - self.link = link + self._link = link + self._source_link = source_link self._factory = factory self._ireq = ireq self._name = name self._version = version self._dist = None # type: Optional[Distribution] + self._prepared = False def __repr__(self): # type: () -> str return "{class_name}({link!r})".format( class_name=self.__class__.__name__, - link=str(self.link), + link=str(self._link), ) + def __hash__(self): + # type: () -> int + return hash((self.__class__, self._link)) + def __eq__(self, other): # type: (Any) -> bool if isinstance(other, self.__class__): - return self.link == other.link + return self._link == other._link return False # Needed for Python 2, which does not implement this by default @@ -128,6 +172,11 @@ class _InstallRequirementBackedCandidate(Candidate): # type: (Any) -> bool return not self.__eq__(other) + @property + def source_link(self): + # type: () -> Optional[Link] + return self._source_link + @property def name(self): # type: () -> str @@ -143,44 +192,73 @@ class _InstallRequirementBackedCandidate(Candidate): self._version = self.dist.parsed_version return self._version + def format_for_error(self): + # type: () -> str + return "{} {} (from {})".format( + self.name, + self.version, + self._link.file_path if self._link.is_file else self._link + ) + def _prepare_abstract_distribution(self): # type: () -> AbstractDistribution raise NotImplementedError("Override in subclass") - def _prepare(self): + def _check_metadata_consistency(self): # type: () -> None - if self._dist is not None: - return - - abstract_dist = self._prepare_abstract_distribution() - self._dist = abstract_dist.get_pkg_resources_distribution() - assert self._dist is not None, "Distribution already installed" - - # TODO: Abort cleanly here, as the resolution has been - # based on the wrong name/version until now, and - # so is wrong. + """Check for consistency of project name and version of dist.""" # TODO: (Longer term) Rather than abort, reject this candidate # and backtrack. This would need resolvelib support. - # These should be "proper" errors, not just asserts, as they - # can result from user errors like a requirement "foo @ URL" - # when the project at URL has a name of "bar" in its metadata. - assert ( - self._name is None or - self._name == canonicalize_name(self._dist.project_name) - ), "Name mismatch: {!r} vs {!r}".format( - self._name, canonicalize_name(self._dist.project_name), - ) - assert ( - self._version is None or - self._version == self._dist.parsed_version - ), "Version mismatch: {!r} vs {!r}".format( - self._version, self._dist.parsed_version, - ) + dist = self._dist # type: Distribution + name = canonicalize_name(dist.project_name) + if self._name is not None and self._name != name: + raise MetadataInconsistent(self._ireq, "name", dist.project_name) + version = dist.parsed_version + if self._version is not None and self._version != version: + raise MetadataInconsistent(self._ireq, "version", dist.version) + + def _prepare(self): + # type: () -> None + if self._prepared: + return + try: + abstract_dist = self._prepare_abstract_distribution() + except HashError as e: + e.req = self._ireq + raise + + self._dist = abstract_dist.get_pkg_resources_distribution() + assert self._dist is not None, "Distribution already installed" + self._check_metadata_consistency() + self._prepared = True + + def _fetch_metadata(self): + # type: () -> None + """Fetch metadata, using lazy wheel if possible.""" + preparer = self._factory.preparer + use_lazy_wheel = self._factory.use_lazy_wheel + remote_wheel = self._link.is_wheel and not self._link.is_file + if use_lazy_wheel and remote_wheel and not preparer.require_hashes: + assert self._name is not None + logger.info('Collecting %s', self._ireq.req or self._ireq) + # If HTTPRangeRequestUnsupported is raised, fallback silently. + with indent_log(), suppress(HTTPRangeRequestUnsupported): + logger.info( + 'Obtaining dependency information from %s %s', + self._name, self._version, + ) + url = self._link.url.split('#', 1)[0] + session = preparer.downloader._session + self._dist = dist_from_wheel_url(self._name, url, session) + self._check_metadata_consistency() + if self._dist is None: + self._prepare() @property def dist(self): # type: () -> Distribution - self._prepare() + if self._dist is None: + self._fetch_metadata() return self._dist def _get_requires_python_specifier(self): @@ -197,18 +275,17 @@ class _InstallRequirementBackedCandidate(Candidate): return None return spec - def get_dependencies(self): - # type: () -> Sequence[Requirement] - deps = [ - self._factory.make_requirement_from_spec(str(r), self._ireq) - for r in self.dist.requires() - ] + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + if not with_requires: + return + for r in self.dist.requires(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) python_dep = self._factory.make_requires_python_requirement( self._get_requires_python_specifier(), ) if python_dep: - deps.append(python_dep) - return deps + yield python_dep def get_install_requirement(self): # type: () -> Optional[InstallRequirement] @@ -217,18 +294,33 @@ class _InstallRequirementBackedCandidate(Candidate): class LinkCandidate(_InstallRequirementBackedCandidate): + is_editable = False + def __init__( self, link, # type: Link - parent, # type: InstallRequirement + template, # type: InstallRequirement factory, # type: Factory name=None, # type: Optional[str] version=None, # type: Optional[_BaseVersion] ): # type: (...) -> None + source_link = link + cache_entry = factory.get_wheel_cache_entry(link, name) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + link = cache_entry.link + ireq = make_install_req_from_link(link, template) + + if (cache_entry is not None and + cache_entry.persistent and + template.link is template.original_link): + ireq.original_link_is_in_wheel_cache = True + super(LinkCandidate, self).__init__( link=link, - ireq=make_install_req_from_link(link, parent), + source_link=source_link, + ireq=ireq, factory=factory, name=name, version=version, @@ -236,14 +328,18 @@ class LinkCandidate(_InstallRequirementBackedCandidate): def _prepare_abstract_distribution(self): # type: () -> AbstractDistribution - return self._factory.preparer.prepare_linked_requirement(self._ireq) + return self._factory.preparer.prepare_linked_requirement( + self._ireq, parallel_builds=True, + ) class EditableCandidate(_InstallRequirementBackedCandidate): + is_editable = True + def __init__( self, link, # type: Link - parent, # type: InstallRequirement + template, # type: InstallRequirement factory, # type: Factory name=None, # type: Optional[str] version=None, # type: Optional[_BaseVersion] @@ -251,7 +347,8 @@ class EditableCandidate(_InstallRequirementBackedCandidate): # type: (...) -> None super(EditableCandidate, self).__init__( link=link, - ireq=make_install_req_from_editable(link, parent), + source_link=link, + ireq=make_install_req_from_editable(link, template), factory=factory, name=name, version=version, @@ -263,15 +360,18 @@ class EditableCandidate(_InstallRequirementBackedCandidate): class AlreadyInstalledCandidate(Candidate): + is_installed = True + source_link = None + def __init__( self, dist, # type: Distribution - parent, # type: InstallRequirement + template, # type: InstallRequirement factory, # type: Factory ): # type: (...) -> None self.dist = dist - self._ireq = make_install_req_from_dist(dist, parent) + self._ireq = make_install_req_from_dist(dist, template) self._factory = factory # This is just logging some messages, so we can do it eagerly. @@ -288,6 +388,10 @@ class AlreadyInstalledCandidate(Candidate): distribution=self.dist, ) + def __hash__(self): + # type: () -> int + return hash((self.__class__, self.name, self.version)) + def __eq__(self, other): # type: (Any) -> bool if isinstance(other, self.__class__): @@ -309,12 +413,21 @@ class AlreadyInstalledCandidate(Candidate): # type: () -> _BaseVersion return self.dist.parsed_version - def get_dependencies(self): - # type: () -> Sequence[Requirement] - return [ - self._factory.make_requirement_from_spec(str(r), self._ireq) - for r in self.dist.requires() - ] + @property + def is_editable(self): + # type: () -> bool + return dist_is_editable(self.dist) + + def format_for_error(self): + # type: () -> str + return "{} {} (Installed)".format(self.name, self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + if not with_requires: + return + for r in self.dist.requires(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) def get_install_requirement(self): # type: () -> Optional[InstallRequirement] @@ -335,8 +448,8 @@ class ExtrasCandidate(Candidate): to treat it as a separate node in the dependency graph. 2. When we're getting the candidate's dependencies, a) We specify that we want the extra dependencies as well. - b) We add a dependency on the base candidate (matching the name and - version). See below for why this is needed. + b) We add a dependency on the base candidate. + See below for why this is needed. 3. We return None for the underlying InstallRequirement, as the base candidate will provide it, and we don't want to end up with duplicates. @@ -348,7 +461,7 @@ class ExtrasCandidate(Candidate): def __init__( self, base, # type: BaseCandidate - extras, # type: Set[str] + extras, # type: FrozenSet[str] ): # type: (...) -> None self.base = base @@ -362,6 +475,10 @@ class ExtrasCandidate(Candidate): extras=self.extras, ) + def __hash__(self): + # type: () -> int + return hash((self.base, self.extras)) + def __eq__(self, other): # type: (Any) -> bool if isinstance(other, self.__class__): @@ -384,30 +501,56 @@ class ExtrasCandidate(Candidate): # type: () -> _BaseVersion return self.base.version - def get_dependencies(self): - # type: () -> Sequence[Requirement] + def format_for_error(self): + # type: () -> str + return "{} [{}]".format( + self.base.format_for_error(), + ", ".join(sorted(self.extras)) + ) + + @property + def is_installed(self): + # type: () -> bool + return self.base.is_installed + + @property + def is_editable(self): + # type: () -> bool + return self.base.is_editable + + @property + def source_link(self): + # type: () -> Optional[Link] + return self.base.source_link + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] factory = self.base._factory + # Add a dependency on the exact base + # (See note 2b in the class docstring) + yield factory.make_requirement_from_candidate(self.base) + if not with_requires: + return + # The user may have specified extras that the candidate doesn't # support. We ignore any unsupported extras here. valid_extras = self.extras.intersection(self.base.dist.extras) invalid_extras = self.extras.difference(self.base.dist.extras) - if invalid_extras: + for extra in sorted(invalid_extras): logger.warning( - "Invalid extras specified in %s: %s", - self.name, - ','.join(sorted(invalid_extras)) + "%s %s does not provide the extra '%s'", + self.base.name, + self.version, + extra ) - deps = [ - factory.make_requirement_from_spec(str(r), self.base._ireq) - for r in self.base.dist.requires(valid_extras) - ] - # Add a dependency on the exact base. - # (See note 2b in the class docstring) - spec = "{}=={}".format(self.base.name, self.base.version) - deps.append(factory.make_requirement_from_spec(spec, self.base._ireq)) - return deps + for r in self.base.dist.requires(valid_extras): + requirement = factory.make_requirement_from_spec( + str(r), self.base._ireq, valid_extras, + ) + if requirement: + yield requirement def get_install_requirement(self): # type: () -> Optional[InstallRequirement] @@ -418,6 +561,9 @@ class ExtrasCandidate(Candidate): class RequiresPythonCandidate(Candidate): + is_installed = False + source_link = None + def __init__(self, py_version_info): # type: (Optional[Tuple[int, ...]]) -> None if py_version_info is not None: @@ -434,16 +580,20 @@ class RequiresPythonCandidate(Candidate): def name(self): # type: () -> str # Avoid conflicting with the PyPI package "Python". - return "" + return "" @property def version(self): # type: () -> _BaseVersion return self._version - def get_dependencies(self): - # type: () -> Sequence[Requirement] - return [] + def format_for_error(self): + # type: () -> str + return "Python {}".format(self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + return () def get_install_requirement(self): # type: () -> Optional[InstallRequirement] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py index 23686f7..dab23aa 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -1,11 +1,26 @@ +import collections +import logging + +from pip._vendor import six from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import ( + DistributionNotFound, InstallationError, UnsupportedPythonVersion, + UnsupportedWheel, +) +from pip._internal.models.wheel import Wheel +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + dist_in_site_packages, + dist_in_usersite, + get_installed_distributions, ) -from pip._internal.utils.misc import get_installed_distributions from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv from .candidates import ( AlreadyInstalledCandidate, @@ -21,17 +36,27 @@ from .requirements import ( ) if MYPY_CHECK_RUNNING: - from typing import Dict, Iterator, Optional, Set, Tuple, TypeVar + from typing import ( + FrozenSet, + Dict, + Iterable, + List, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + ) from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.version import _BaseVersion from pip._vendor.pkg_resources import Distribution from pip._vendor.resolvelib import ResolutionImpossible + from pip._internal.cache import CacheEntry, WheelCache from pip._internal.index.package_finder import PackageFinder from pip._internal.models.link import Link from pip._internal.operations.prepare import RequirementPreparer - from pip._internal.req.req_install import InstallRequirement from pip._internal.resolution.base import InstallRequirementProvider from .base import Candidate, Requirement @@ -39,6 +64,10 @@ if MYPY_CHECK_RUNNING: C = TypeVar("C") Cache = Dict[Link, C] + VersionCandidates = Dict[_BaseVersion, Candidate] + + +logger = logging.getLogger(__name__) class Factory(object): @@ -47,18 +76,24 @@ class Factory(object): finder, # type: PackageFinder preparer, # type: RequirementPreparer make_install_req, # type: InstallRequirementProvider + wheel_cache, # type: Optional[WheelCache] + use_user_site, # type: bool force_reinstall, # type: bool ignore_installed, # type: bool ignore_requires_python, # type: bool py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool ): # type: (...) -> None - self.finder = finder + self._finder = finder self.preparer = preparer + self._wheel_cache = wheel_cache self._python_candidate = RequiresPythonCandidate(py_version_info) self._make_install_req_from_spec = make_install_req + self._use_user_site = use_user_site self._force_reinstall = force_reinstall self._ignore_requires_python = ignore_requires_python + self.use_lazy_wheel = lazy_wheel self._link_candidate_cache = {} # type: Cache[LinkCandidate] self._editable_candidate_cache = {} # type: Cache[EditableCandidate] @@ -71,96 +106,186 @@ class Factory(object): else: self._installed_dists = {} + @property + def force_reinstall(self): + # type: () -> bool + return self._force_reinstall + def _make_candidate_from_dist( self, dist, # type: Distribution - extras, # type: Set[str] - parent, # type: InstallRequirement + extras, # type: FrozenSet[str] + template, # type: InstallRequirement ): # type: (...) -> Candidate - base = AlreadyInstalledCandidate(dist, parent, factory=self) + base = AlreadyInstalledCandidate(dist, template, factory=self) if extras: return ExtrasCandidate(base, extras) return base def _make_candidate_from_link( self, - link, # type: Link - extras, # type: Set[str] - parent, # type: InstallRequirement - name=None, # type: Optional[str] - version=None, # type: Optional[_BaseVersion] + link, # type: Link + extras, # type: FrozenSet[str] + template, # type: InstallRequirement + name, # type: Optional[str] + version, # type: Optional[_BaseVersion] ): # type: (...) -> Candidate # TODO: Check already installed candidate, and use it if the link and # editable flag match. - if parent.editable: + if template.editable: if link not in self._editable_candidate_cache: self._editable_candidate_cache[link] = EditableCandidate( - link, parent, factory=self, name=name, version=version, + link, template, factory=self, name=name, version=version, ) base = self._editable_candidate_cache[link] # type: BaseCandidate else: if link not in self._link_candidate_cache: self._link_candidate_cache[link] = LinkCandidate( - link, parent, factory=self, name=name, version=version, + link, template, factory=self, name=name, version=version, ) base = self._link_candidate_cache[link] if extras: return ExtrasCandidate(base, extras) return base - def iter_found_candidates(self, ireq, extras): - # type: (InstallRequirement, Set[str]) -> Iterator[Candidate] - name = canonicalize_name(ireq.req.name) - if not self._force_reinstall: - installed_dist = self._installed_dists.get(name) - else: - installed_dist = None + def _iter_found_candidates( + self, + ireqs, # type: Sequence[InstallRequirement] + specifier, # type: SpecifierSet + ): + # type: (...) -> Iterable[Candidate] + if not ireqs: + return () - found = self.finder.find_best_candidate( - project_name=ireq.req.name, - specifier=ireq.req.specifier, - hashes=ireq.hashes(trust_internet=False), + # The InstallRequirement implementation requires us to give it a + # "template". Here we just choose the first requirement to represent + # all of them. + # Hopefully the Project model can correct this mismatch in the future. + template = ireqs[0] + name = canonicalize_name(template.req.name) + + hashes = Hashes() + extras = frozenset() # type: FrozenSet[str] + for ireq in ireqs: + specifier &= ireq.req.specifier + hashes |= ireq.hashes(trust_internet=False) + extras |= frozenset(ireq.extras) + + # We use this to ensure that we only yield a single candidate for + # each version (the finder's preferred one for that version). The + # requirement needs to return only one candidate per version, so we + # implement that logic here so that requirements using this helper + # don't all have to do the same thing later. + candidates = collections.OrderedDict() # type: VersionCandidates + + # Get the installed version, if it matches, unless the user + # specified `--force-reinstall`, when we want the version from + # the index instead. + installed_version = None + installed_candidate = None + if not self._force_reinstall and name in self._installed_dists: + installed_dist = self._installed_dists[name] + installed_version = installed_dist.parsed_version + if specifier.contains(installed_version, prereleases=True): + installed_candidate = self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + template=template, + ) + + found = self._finder.find_best_candidate( + project_name=name, + specifier=specifier, + hashes=hashes, ) for ican in found.iter_applicable(): - if (installed_dist is not None and - installed_dist.parsed_version == ican.version): - continue - yield self._make_candidate_from_link( - link=ican.link, - extras=extras, - parent=ireq, - name=name, - version=ican.version, + if ican.version == installed_version and installed_candidate: + candidate = installed_candidate + else: + candidate = self._make_candidate_from_link( + link=ican.link, + extras=extras, + template=template, + name=name, + version=ican.version, + ) + candidates[ican.version] = candidate + + # Yield the installed version even if it is not found on the index. + if installed_version and installed_candidate: + candidates[installed_version] = installed_candidate + + return six.itervalues(candidates) + + def find_candidates(self, requirements, constraint): + # type: (Sequence[Requirement], SpecifierSet) -> Iterable[Candidate] + explicit_candidates = set() # type: Set[Candidate] + ireqs = [] # type: List[InstallRequirement] + for req in requirements: + cand, ireq = req.get_candidate_lookup() + if cand is not None: + explicit_candidates.add(cand) + if ireq is not None: + ireqs.append(ireq) + + # If none of the requirements want an explicit candidate, we can ask + # the finder for candidates. + if not explicit_candidates: + return self._iter_found_candidates(ireqs, constraint) + + if constraint: + name = explicit_candidates.pop().name + raise InstallationError( + "Could not satisfy constraints for {!r}: installation from " + "path or url cannot be constrained to a version".format(name) ) - # Return installed distribution if it matches the specifier. This is - # done last so the resolver will prefer it over downloading links. - if (installed_dist is not None and - installed_dist.parsed_version in ireq.req.specifier): - yield self._make_candidate_from_dist( - dist=installed_dist, - extras=extras, - parent=ireq, - ) + return ( + c for c in explicit_candidates + if all(req.is_satisfied_by(c) for req in requirements) + ) - def make_requirement_from_install_req(self, ireq): - # type: (InstallRequirement) -> Requirement - if ireq.link: - # TODO: Get name and version from ireq, if possible? - # Specifically, this might be needed in "name @ URL" - # syntax - need to check where that syntax is handled. - cand = self._make_candidate_from_link( - ireq.link, extras=set(), parent=ireq, + def make_requirement_from_install_req(self, ireq, requested_extras): + # type: (InstallRequirement, Iterable[str]) -> Optional[Requirement] + if not ireq.match_markers(requested_extras): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + ireq.name, ireq.markers, ) - return ExplicitRequirement(cand) - return SpecifierRequirement(ireq, factory=self) + return None + if not ireq.link: + return SpecifierRequirement(ireq) + if ireq.link.is_wheel: + wheel = Wheel(ireq.link.filename) + if not wheel.supported(self._finder.target_python.get_tags()): + msg = "{} is not a supported wheel on this platform.".format( + wheel.filename, + ) + raise UnsupportedWheel(msg) + cand = self._make_candidate_from_link( + ireq.link, + extras=frozenset(ireq.extras), + template=ireq, + name=canonicalize_name(ireq.name) if ireq.name else None, + version=None, + ) + return self.make_requirement_from_candidate(cand) - def make_requirement_from_spec(self, specifier, comes_from): - # type: (str, InstallRequirement) -> Requirement + def make_requirement_from_candidate(self, candidate): + # type: (Candidate) -> ExplicitRequirement + return ExplicitRequirement(candidate) + + def make_requirement_from_spec( + self, + specifier, # type: str + comes_from, # type: InstallRequirement + requested_extras=(), # type: Iterable[str] + ): + # type: (...) -> Optional[Requirement] ireq = self._make_install_req_from_spec(specifier, comes_from) - return self.make_requirement_from_install_req(ireq) + return self.make_requirement_from_install_req(ireq, requested_extras) def make_requires_python_requirement(self, specifier): # type: (Optional[SpecifierSet]) -> Optional[Requirement] @@ -168,34 +293,167 @@ class Factory(object): return None return RequiresPythonRequirement(specifier, self._python_candidate) - def should_reinstall(self, candidate): - # type: (Candidate) -> bool + def get_wheel_cache_entry(self, link, name): + # type: (Link, Optional[str]) -> Optional[CacheEntry] + """Look up the link in the wheel cache. + + If ``preparer.require_hashes`` is True, don't use the wheel cache, + because cached wheels, always built locally, have different hashes + than the files downloaded from the index server and thus throw false + hash mismatches. Furthermore, cached wheels at present have + nondeterministic contents due to file modification times. + """ + if self._wheel_cache is None or self.preparer.require_hashes: + return None + return self._wheel_cache.get_cache_entry( + link=link, + package_name=name, + supported_tags=get_supported(), + ) + + def get_dist_to_uninstall(self, candidate): + # type: (Candidate) -> Optional[Distribution] # TODO: Are there more cases this needs to return True? Editable? - return candidate.name in self._installed_dists + dist = self._installed_dists.get(candidate.name) + if dist is None: # Not installed, no uninstallation required. + return None + + # We're installing into global site. The current installation must + # be uninstalled, no matter it's in global or user site, because the + # user site installation has precedence over global. + if not self._use_user_site: + return dist + + # We're installing into user site. Remove the user site installation. + if dist_in_usersite(dist): + return dist + + # We're installing into user site, but the installed incompatible + # package is in global site. We can't uninstall that, and would let + # the new user installation to "shadow" it. But shadowing won't work + # in virtual environments, so we error out. + if running_under_virtualenv() and dist_in_site_packages(dist): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to {} in {}".format( + dist.project_name, dist.location, + ) + ) + return None def _report_requires_python_error( self, requirement, # type: RequiresPythonRequirement - parent, # type: Candidate + template, # type: Candidate ): # type: (...) -> UnsupportedPythonVersion - template = ( + message_format = ( "Package {package!r} requires a different Python: " "{version} not in {specifier!r}" ) - message = template.format( - package=parent.name, + message = message_format.format( + package=template.name, version=self._python_candidate.version, specifier=str(requirement.specifier), ) return UnsupportedPythonVersion(message) def get_installation_error(self, e): - # type: (ResolutionImpossible) -> Optional[InstallationError] + # type: (ResolutionImpossible) -> InstallationError + + assert e.causes, "Installation error reported with no cause" + + # If one of the things we can't solve is "we need Python X.Y", + # that is what we report. for cause in e.causes: if isinstance(cause.requirement, RequiresPythonRequirement): return self._report_requires_python_error( cause.requirement, cause.parent, ) - return None + + # Otherwise, we have a set of causes which can't all be satisfied + # at once. + + # The simplest case is when we have *one* cause that can't be + # satisfied. We just report that case. + if len(e.causes) == 1: + req, parent = e.causes[0] + if parent is None: + req_disp = str(req) + else: + req_disp = '{} (from {})'.format(req, parent.name) + logger.critical( + "Could not find a version that satisfies the requirement %s", + req_disp, + ) + return DistributionNotFound( + 'No matching distribution found for {}'.format(req) + ) + + # OK, we now have a list of requirements that can't all be + # satisfied at once. + + # A couple of formatting helpers + def text_join(parts): + # type: (List[str]) -> str + if len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def readable_form(cand): + # type: (Candidate) -> str + return "{} {}".format(cand.name, cand.version) + + def describe_trigger(parent): + # type: (Candidate) -> str + ireq = parent.get_install_requirement() + if not ireq or not ireq.comes_from: + return "{} {}".format(parent.name, parent.version) + if isinstance(ireq.comes_from, InstallRequirement): + return str(ireq.comes_from.name) + return str(ireq.comes_from) + + triggers = [] + for req, parent in e.causes: + if parent is None: + # This is a root requirement, so we can report it directly + trigger = req.format_for_error() + else: + trigger = describe_trigger(parent) + triggers.append(trigger) + + if triggers: + info = text_join(triggers) + else: + info = "the requested packages" + + msg = "Cannot install {} because these package versions " \ + "have conflicting dependencies.".format(info) + logger.critical(msg) + msg = "\nThe conflict is caused by:" + for req, parent in e.causes: + msg = msg + "\n " + if parent: + msg = msg + "{} {} depends on ".format( + parent.name, + parent.version + ) + else: + msg = msg + "The user requested " + msg = msg + req.format_for_error() + + msg = msg + "\n\n" + \ + "To fix this you could try to:\n" + \ + "1. loosen the range of package versions you've specified\n" + \ + "2. remove package versions to allow pip attempt to solve " + \ + "the dependency conflict\n" + + logger.info(msg) + + return DistributionNotFound( + "ResolutionImpossible: for help visit " + "https://pip.pypa.io/en/latest/user_guide/" + "#fixing-conflicting-dependencies" + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py index 5c3d210..b2eb9d0 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -1,29 +1,119 @@ +from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.resolvelib.providers import AbstractProvider from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Any, Optional, Sequence, Tuple, Union - - from pip._internal.req.req_install import InstallRequirement + from typing import ( + Any, + Dict, + Iterable, + Optional, + Sequence, + Set, + Tuple, + Union, + ) from .base import Requirement, Candidate from .factory import Factory +# Notes on the relationship between the provider, the factory, and the +# candidate and requirement classes. +# +# The provider is a direct implementation of the resolvelib class. Its role +# is to deliver the API that resolvelib expects. +# +# Rather than work with completely abstract "requirement" and "candidate" +# concepts as resolvelib does, pip has concrete classes implementing these two +# ideas. The API of Requirement and Candidate objects are defined in the base +# classes, but essentially map fairly directly to the equivalent provider +# methods. In particular, `find_matches` and `is_satisfied_by` are +# requirement methods, and `get_dependencies` is a candidate method. +# +# The factory is the interface to pip's internal mechanisms. It is stateless, +# and is created by the resolver and held as a property of the provider. It is +# responsible for creating Requirement and Candidate objects, and provides +# services to those objects (access to pip's finder and preparer). + class PipProvider(AbstractProvider): def __init__( self, factory, # type: Factory + constraints, # type: Dict[str, SpecifierSet] ignore_dependencies, # type: bool + upgrade_strategy, # type: str + user_requested, # type: Set[str] ): # type: (...) -> None self._factory = factory + self._constraints = constraints self._ignore_dependencies = ignore_dependencies + self._upgrade_strategy = upgrade_strategy + self.user_requested = user_requested - def get_install_requirement(self, c): - # type: (Candidate) -> Optional[InstallRequirement] - return c.get_install_requirement() + def _sort_matches(self, matches): + # type: (Iterable[Candidate]) -> Sequence[Candidate] + + # The requirement is responsible for returning a sequence of potential + # candidates, one per version. The provider handles the logic of + # deciding the order in which these candidates should be passed to + # the resolver. + + # The `matches` argument is a sequence of candidates, one per version, + # which are potential options to be installed. The requirement will + # have already sorted out whether to give us an already-installed + # candidate or a version from PyPI (i.e., it will deal with options + # like --force-reinstall and --ignore-installed). + + # We now work out the correct order. + # + # 1. If no other considerations apply, later versions take priority. + # 2. An already installed distribution is preferred over any other, + # unless the user has requested an upgrade. + # Upgrades are allowed when: + # * The --upgrade flag is set, and + # - The project was specified on the command line, or + # - The project is a dependency and the "eager" upgrade strategy + # was requested. + def _eligible_for_upgrade(name): + # type: (str) -> bool + """Are upgrades allowed for this project? + + This checks the upgrade strategy, and whether the project was one + that the user specified in the command line, in order to decide + whether we should upgrade if there's a newer version available. + + (Note that we don't need access to the `--upgrade` flag, because + an upgrade strategy of "to-satisfy-only" means that `--upgrade` + was not specified). + """ + if self._upgrade_strategy == "eager": + return True + elif self._upgrade_strategy == "only-if-needed": + return (name in self.user_requested) + return False + + def sort_key(c): + # type: (Candidate) -> int + """Return a sort key for the matches. + + The highest priority should be given to installed candidates that + are not eligible for upgrade. We use the integer value in the first + part of the key to sort these before other candidates. + + We only pull the installed candidate to the bottom (i.e. most + preferred), but otherwise keep the ordering returned by the + requirement. The requirement is responsible for returning a list + otherwise sorted for the resolver, taking account for versions + and binary preferences as specified by the user. + """ + if c.is_installed and not _eligible_for_upgrade(c.name): + return 1 + return 0 + + return sorted(matches, key=sort_key) def identify(self, dependency): # type: (Union[Requirement, Candidate]) -> str @@ -39,9 +129,15 @@ class PipProvider(AbstractProvider): # Use the "usual" value for now return len(candidates) - def find_matches(self, requirement): - # type: (Requirement) -> Sequence[Candidate] - return requirement.find_matches() + def find_matches(self, requirements): + # type: (Sequence[Requirement]) -> Iterable[Candidate] + if not requirements: + return [] + constraint = self._constraints.get( + requirements[0].name, SpecifierSet(), + ) + candidates = self._factory.find_candidates(requirements, constraint) + return reversed(self._sort_matches(candidates)) def is_satisfied_by(self, requirement, candidate): # type: (Requirement, Candidate) -> bool @@ -49,6 +145,9 @@ class PipProvider(AbstractProvider): def get_dependencies(self, candidate): # type: (Candidate) -> Sequence[Requirement] - if self._ignore_dependencies: - return [] - return candidate.get_dependencies() + with_requires = not self._ignore_dependencies + return [ + r + for r in candidate.iter_dependencies(with_requires) + if r is not None + ] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py index d2e4479..bc1061f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -5,14 +5,11 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING from .base import Requirement, format_name if MYPY_CHECK_RUNNING: - from typing import Sequence - from pip._vendor.packaging.specifiers import SpecifierSet from pip._internal.req.req_install import InstallRequirement - from .base import Candidate - from .factory import Factory + from .base import Candidate, CandidateLookup class ExplicitRequirement(Requirement): @@ -33,9 +30,13 @@ class ExplicitRequirement(Requirement): # No need to canonicalise - the candidate did this return self.candidate.name - def find_matches(self): - # type: () -> Sequence[Candidate] - return [self.candidate] + def format_for_error(self): + # type: () -> str + return self.candidate.format_for_error() + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return self.candidate, None def is_satisfied_by(self, candidate): # type: (Candidate) -> bool @@ -43,12 +44,11 @@ class ExplicitRequirement(Requirement): class SpecifierRequirement(Requirement): - def __init__(self, ireq, factory): - # type: (InstallRequirement, Factory) -> None + def __init__(self, ireq): + # type: (InstallRequirement) -> None assert ireq.link is None, "This is a link, not a specifier" self._ireq = ireq - self._factory = factory - self.extras = ireq.req.extras + self._extras = frozenset(ireq.extras) def __str__(self): # type: () -> str @@ -65,12 +65,26 @@ class SpecifierRequirement(Requirement): def name(self): # type: () -> str canonical_name = canonicalize_name(self._ireq.req.name) - return format_name(canonical_name, self.extras) + return format_name(canonical_name, self._extras) - def find_matches(self): - # type: () -> Sequence[Candidate] - it = self._factory.iter_found_candidates(self._ireq, self.extras) - return list(it) + def format_for_error(self): + # type: () -> str + + # Convert comma-separated specifiers into "A, B, ..., F and G" + # This makes the specifier a bit more "human readable", without + # risking a change in meaning. (Hopefully! Not all edge cases have + # been checked) + parts = [s.strip() for s in str(self).split(",")] + if len(parts) == 0: + return "" + elif len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return None, self._ireq def is_satisfied_by(self, candidate): # type: (Candidate) -> bool @@ -104,11 +118,15 @@ class RequiresPythonRequirement(Requirement): # type: () -> str return self._candidate.name - def find_matches(self): - # type: () -> Sequence[Candidate] - if self._candidate.version in self.specifier: - return [self._candidate] - return [] + def format_for_error(self): + # type: () -> str + return "Python " + str(self.specifier) + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + if self.specifier.contains(self._candidate.version, prereleases=True): + return self._candidate, None + return None, None def is_satisfied_by(self, candidate): # type: (Candidate) -> bool diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py index cba5a49..aecddb1 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -7,17 +7,21 @@ from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible from pip._vendor.resolvelib import Resolver as RLResolver from pip._internal.exceptions import InstallationError +from pip._internal.req.req_install import check_invalid_constraint_type from pip._internal.req.req_set import RequirementSet from pip._internal.resolution.base import BaseResolver from pip._internal.resolution.resolvelib.provider import PipProvider +from pip._internal.utils.misc import dist_is_editable from pip._internal.utils.typing import MYPY_CHECK_RUNNING from .factory import Factory if MYPY_CHECK_RUNNING: - from typing import Dict, List, Optional, Tuple + from typing import Dict, List, Optional, Set, Tuple + from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.resolvelib.resolvers import Result + from pip._vendor.resolvelib.structs import Graph from pip._internal.cache import WheelCache from pip._internal.index.package_finder import PackageFinder @@ -30,6 +34,8 @@ logger = logging.getLogger(__name__) class Resolver(BaseResolver): + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + def __init__( self, preparer, # type: RequirementPreparer @@ -43,113 +49,146 @@ class Resolver(BaseResolver): force_reinstall, # type: bool upgrade_strategy, # type: str py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool ): super(Resolver, self).__init__() + if lazy_wheel: + logger.warning( + 'pip is using lazily downloaded wheels using HTTP ' + 'range requests to obtain dependency information. ' + 'This experimental feature is enabled through ' + '--use-feature=fast-deps and it is not ready for production.' + ) + + assert upgrade_strategy in self._allowed_strategies + self.factory = Factory( finder=finder, preparer=preparer, make_install_req=make_install_req, + wheel_cache=wheel_cache, + use_user_site=use_user_site, force_reinstall=force_reinstall, ignore_installed=ignore_installed, ignore_requires_python=ignore_requires_python, py_version_info=py_version_info, + lazy_wheel=lazy_wheel, ) self.ignore_dependencies = ignore_dependencies + self.upgrade_strategy = upgrade_strategy self._result = None # type: Optional[Result] def resolve(self, root_reqs, check_supported_wheels): # type: (List[InstallRequirement], bool) -> RequirementSet - # FIXME: Implement constraints. - if any(r.constraint for r in root_reqs): - raise InstallationError("Constraints are not yet supported.") + constraints = {} # type: Dict[str, SpecifierSet] + user_requested = set() # type: Set[str] + requirements = [] + for req in root_reqs: + if req.constraint: + # Ensure we only accept valid constraints + problem = check_invalid_constraint_type(req) + if problem: + raise InstallationError(problem) + if not req.match_markers(): + continue + name = canonicalize_name(req.name) + if name in constraints: + constraints[name] = constraints[name] & req.specifier + else: + constraints[name] = req.specifier + else: + if req.user_supplied and req.name: + user_requested.add(canonicalize_name(req.name)) + r = self.factory.make_requirement_from_install_req( + req, requested_extras=(), + ) + if r is not None: + requirements.append(r) provider = PipProvider( factory=self.factory, + constraints=constraints, ignore_dependencies=self.ignore_dependencies, + upgrade_strategy=self.upgrade_strategy, + user_requested=user_requested, ) reporter = BaseReporter() resolver = RLResolver(provider, reporter) - requirements = [ - self.factory.make_requirement_from_install_req(r) - for r in root_reqs - ] - try: - self._result = resolver.resolve(requirements) + try_to_avoid_resolution_too_deep = 2000000 + self._result = resolver.resolve( + requirements, max_rounds=try_to_avoid_resolution_too_deep, + ) except ResolutionImpossible as e: error = self.factory.get_installation_error(e) - if not error: - # TODO: This needs fixing, we need to look at the - # factory.get_installation_error infrastructure, as that - # doesn't really allow for the logger.critical calls I'm - # using here. - for req, parent in e.causes: - logger.critical( - "Could not find a version that satisfies " + - "the requirement " + - str(req) + - ("" if parent is None else " (from {})".format( - parent.name - )) - ) - raise InstallationError( - "No matching distribution found for " + - ", ".join([r.name for r, _ in e.causes]) - ) - raise six.raise_from(error, e) req_set = RequirementSet(check_supported_wheels=check_supported_wheels) for candidate in self._result.mapping.values(): - ireq = provider.get_install_requirement(candidate) + ireq = candidate.get_install_requirement() if ireq is None: continue - ireq.should_reinstall = self.factory.should_reinstall(candidate) + + # Check if there is already an installation under the same name, + # and set a flag for later stages to uninstall it, if needed. + # * There isn't, good -- no uninstalltion needed. + # * The --force-reinstall flag is set. Always reinstall. + # * The installation is different in version or editable-ness, so + # we need to uninstall it to install the new distribution. + # * The installed version is the same as the pending distribution. + # Skip this distrubiton altogether to save work. + installed_dist = self.factory.get_dist_to_uninstall(candidate) + if installed_dist is None: + ireq.should_reinstall = False + elif self.factory.force_reinstall: + ireq.should_reinstall = True + elif installed_dist.parsed_version != candidate.version: + ireq.should_reinstall = True + elif dist_is_editable(installed_dist) != candidate.is_editable: + ireq.should_reinstall = True + else: + continue + + link = candidate.source_link + if link and link.is_yanked: + # The reason can contain non-ASCII characters, Unicode + # is required for Python 2. + msg = ( + u'The candidate selected for download or install is a ' + u'yanked version: {name!r} candidate (version {version} ' + u'at {link})\nReason for being yanked: {reason}' + ).format( + name=candidate.name, + version=candidate.version, + link=link, + reason=link.yanked_reason or u'', + ) + logger.warning(msg) + req_set.add_named_requirement(ireq) return req_set def get_installation_order(self, req_set): # type: (RequirementSet) -> List[InstallRequirement] - """Create a list that orders given requirements for installation. + """Get order for installation of requirements in RequirementSet. - The returned list should contain all requirements in ``req_set``, - so the caller can loop through it and have a requirement installed - before the requiring thing. + The returned list contains a requirement before another that depends on + it. This helps ensure that the environment is kept consistent as they + get installed one-by-one. - The current implementation walks the resolved dependency graph, and - make sure every node has a greater "weight" than all its parents. + The current implementation creates a topological ordering of the + dependency graph, while breaking any cycles in the graph at arbitrary + points. We make no guarantees about where the cycle would be broken, + other than they would be broken. """ assert self._result is not None, "must call resolve() first" - weights = {} # type: Dict[Optional[str], int] graph = self._result.graph - key_count = len(self._result.mapping) + 1 # Packages plus sentinal. - while len(weights) < key_count: - progressed = False - for key in graph: - if key in weights: - continue - parents = list(graph.iter_parents(key)) - if not all(p in weights for p in parents): - continue - if parents: - weight = max(weights[p] for p in parents) + 1 - else: - weight = 0 - weights[key] = weight - progressed = True - - # FIXME: This check will fail if there are unbreakable cycles. - # Implement something to forcifully break them up to continue. - if not progressed: - raise InstallationError( - "Could not determine installation order due to cicular " - "dependency." - ) + weights = get_topological_weights(graph) sorted_items = sorted( req_set.requirements.items(), @@ -159,6 +198,52 @@ class Resolver(BaseResolver): return [ireq for _, ireq in sorted_items] +def get_topological_weights(graph): + # type: (Graph) -> Dict[Optional[str], int] + """Assign weights to each node based on how "deep" they are. + + This implementation may change at any point in the future without prior + notice. + + We take the length for the longest path to any node from root, ignoring any + paths that contain a single node twice (i.e. cycles). This is done through + a depth-first search through the graph, while keeping track of the path to + the node. + + Cycles in the graph result would result in node being revisited while also + being it's own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + """ + path = set() # type: Set[Optional[str]] + weights = {} # type: Dict[Optional[str], int] + + def visit(node): + # type: (Optional[str]) -> None + if node in path: + # We hit a cycle, so we'll break it here. + return + + # Time to visit the children! + path.add(node) + for child in graph.iter_children(node): + visit(child) + path.remove(node) + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + + # Sanity checks + assert weights[None] == 0 + assert len(weights) == len(graph) + + return weights + + def _req_set_item_sorter( item, # type: Tuple[str, InstallRequirement] weights, # type: Dict[Optional[str], int] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py b/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py index 8fc3c59..fbd9dfd 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import datetime @@ -10,13 +7,11 @@ import logging import os.path import sys -from pip._vendor import pkg_resources from pip._vendor.packaging import version as packaging_version from pip._vendor.six import ensure_binary from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder -from pip._internal.models.search_scope import SearchScope from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.utils.filesystem import ( adjacent_tmp_file, @@ -25,15 +20,14 @@ from pip._internal.utils.filesystem import ( ) from pip._internal.utils.misc import ( ensure_dir, + get_distribution, get_installed_version, - redact_auth_from_url, ) from pip._internal.utils.packaging import get_installer from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: import optparse - from optparse import Values from typing import Any, Dict, Text, Union from pip._internal.network.session import PipSession @@ -45,37 +39,6 @@ SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" logger = logging.getLogger(__name__) -def make_link_collector( - session, # type: PipSession - options, # type: Values - suppress_no_index=False, # type: bool -): - # type: (...) -> LinkCollector - """ - :param session: The Session to use to make requests. - :param suppress_no_index: Whether to ignore the --no-index option - when constructing the SearchScope object. - """ - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index and not suppress_no_index: - logger.debug( - 'Ignoring indexes: %s', - ','.join(redact_auth_from_url(url) for url in index_urls), - ) - index_urls = [] - - # Make sure find_links is a list before passing to create(). - find_links = options.find_links or [] - - search_scope = SearchScope.create( - find_links=find_links, index_urls=index_urls, - ) - - link_collector = LinkCollector(session=session, search_scope=search_scope) - - return link_collector - - def _get_statefile_name(key): # type: (Union[str, Text]) -> str key_bytes = ensure_binary(key) @@ -104,6 +67,7 @@ class SelfCheckState(object): @property def key(self): + # type: () -> str return sys.prefix def save(self, pypi_version, current_time): @@ -149,11 +113,10 @@ def was_installed_by_pip(pkg): This is used not to display the upgrade message when pip is in fact installed by system package manager, such as dnf on Fedora. """ - try: - dist = pkg_resources.get_distribution(pkg) - return "pip" == get_installer(dist) - except pkg_resources.DistributionNotFound: + dist = get_distribution(pkg) + if not dist: return False + return "pip" == get_installer(dist) def pip_self_version_check(session, options): @@ -187,7 +150,7 @@ def pip_self_version_check(session, options): # Refresh the version if we need to or just see if we need to warn if pypi_version is None: # Lets use PackageFinder to see what the latest pip version is - link_collector = make_link_collector( + link_collector = LinkCollector.create( session, options=options, suppress_no_index=True, diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py index d939e21..89c5169 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py @@ -121,10 +121,11 @@ def str_to_display(data, desc=None): try: decoded_data = data.decode(encoding) except UnicodeDecodeError: - if desc is None: - desc = 'Bytes object' - msg_format = '{} does not appear to be encoded as %s'.format(desc) - logger.warning(msg_format, encoding) + logger.warning( + '%s does not appear to be encoded as %s', + desc or 'Bytes object', + encoding, + ) decoded_data = data.decode(encoding, errors=backslashreplace_decode) # Make sure we can print the output, by encoding it to the output diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py index 47d04f0..4f21874 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py @@ -3,7 +3,6 @@ from __future__ import absolute_import -import logging import re from pip._vendor.packaging.tags import ( @@ -23,8 +22,6 @@ if MYPY_CHECK_RUNNING: from pip._vendor.packaging.tags import PythonVersion -logger = logging.getLogger(__name__) - _osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/datetime.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/datetime.py new file mode 100644 index 0000000..4d0503c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/datetime.py @@ -0,0 +1,14 @@ +"""For when pip wants to check the date or time. +""" + +from __future__ import absolute_import + +import datetime + + +def today_is_later_than(year, month, day): + # type: (int, int, int) -> bool + today = datetime.date.today() + given = datetime.date(year, month, day) + + return today > given diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py index ab4d4b9..5b83d61 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import codecs import locale import re @@ -35,7 +32,9 @@ def auto_decode(data): # Lets check the first two lines as in PEP263 for line in data.split(b'\n')[:2]: if line[0:1] == b'#' and ENCODING_RE.search(line): - encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + result = ENCODING_RE.search(line) + assert result is not None + encoding = result.groups()[0].decode('ascii') return data.decode(encoding) return data.decode( locale.getpreferredencoding(False) or sys.getdefaultencoding(), diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py index 437a7fd..303243f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py @@ -155,7 +155,7 @@ def _test_writable_dir_win(path): # and we can't use tempfile: http://bugs.python.org/issue22107 basename = 'accesstest_deleteme_fishfingers_custard_' alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' - for i in range(10): + for _ in range(10): name = basename + ''.join(random.choice(alphabet) for _ in range(6)) file = os.path.join(path, name) try: @@ -170,6 +170,8 @@ def _test_writable_dir_win(path): # This could be because there's a directory with the same name. # But it's highly unlikely there's a directory called that, # so we'll assume it's because the parent dir is not writable. + # This could as well be because the parent dir is not readable, + # due to non-privileged user access. return False raise else: @@ -188,7 +190,7 @@ def find_files(path, pattern): """Returns a list of absolute paths of files beneath path, recursively, with filenames which match the UNIX-style shell glob pattern.""" result = [] # type: List[str] - for root, dirs, files in os.walk(path): + for root, _, files in os.walk(path): matches = fnmatch.filter(files, pattern) result.extend(os.path.join(root, f) for f in matches) return result diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py index 396cf82..d1b062f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py @@ -46,6 +46,18 @@ class Hashes(object): """ self._allowed = {} if hashes is None else hashes + def __or__(self, other): + # type: (Hashes) -> Hashes + if not isinstance(other, Hashes): + return NotImplemented + new = self._allowed.copy() + for alg, values in iteritems(other._allowed): + try: + new[alg] += values + except KeyError: + new[alg] = values + return Hashes(new) + @property def digest_count(self): # type: () -> int diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py index 0903182..5629c60 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py @@ -16,13 +16,15 @@ import shutil import stat import sys from collections import deque +from itertools import tee from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name # NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is # why we ignore the type on this import. from pip._vendor.retrying import retry # type: ignore from pip._vendor.six import PY2, text_type -from pip._vendor.six.moves import input, map, zip_longest +from pip._vendor.six.moves import filter, filterfalse, input, map, zip_longest from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote @@ -52,12 +54,13 @@ else: if MYPY_CHECK_RUNNING: from typing import ( - Any, AnyStr, Container, Iterable, Iterator, List, Optional, Text, - Tuple, Union, + Any, AnyStr, Callable, Container, Iterable, Iterator, List, Optional, + Text, Tuple, TypeVar, Union, ) from pip._vendor.pkg_resources import Distribution VersionInfo = Tuple[int, int, int] + T = TypeVar("T") __all__ = ['rmtree', 'display_path', 'backup_dir', @@ -131,7 +134,7 @@ def get_prog(): # Retry every half second for up to 3 seconds @retry(stop_max_delay=3000, wait_fixed=500) def rmtree(dir, ignore_errors=False): - # type: (str, bool) -> None + # type: (Text, bool) -> None shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler) @@ -480,6 +483,57 @@ def get_installed_distributions( ] +def _search_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Find a distribution matching the ``req_name`` in the environment. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + # Canonicalize the name before searching in the list of + # installed distributions and also while creating the package + # dictionary to get the Distribution object + req_name = canonicalize_name(req_name) + packages = get_installed_distributions( + local_only=False, + skip=(), + include_editables=True, + editables_only=False, + user_only=False, + paths=None, + ) + pkg_dict = {canonicalize_name(p.key): p for p in packages} + return pkg_dict.get(req_name) + + +def get_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Given a requirement name, return the installed Distribution object. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + + # Search the distribution by looking through the working set + dist = _search_distribution(req_name) + + # If distribution could not be found, call working_set.require + # to update the working set, and try to find the distribution + # again. + # This might happen for e.g. when you install a package + # twice, once using setup.py develop and again using setup.py install. + # Now when run pip uninstall twice, the package gets removed + # from the working set in the first uninstall, so we have to populate + # the working set again so that pip knows about it and the packages + # gets picked up and is successfully uninstalled the second time too. + if not dist: + try: + pkg_resources.working_set.require(req_name) + except pkg_resources.DistributionNotFound: + return None + return _search_distribution(req_name) + + def egg_link_path(dist): # type: (Distribution) -> Optional[str] """ @@ -533,7 +587,7 @@ def dist_location(dist): def write_output(msg, *args): - # type: (str, str) -> None + # type: (Any, Any) -> None logger.info(msg, *args) @@ -541,14 +595,11 @@ class FakeFile(object): """Wrap a list of lines in an object with readline() to make ConfigParser happy.""" def __init__(self, lines): - self._gen = (l for l in lines) + self._gen = iter(lines) def readline(self): try: - try: - return next(self._gen) - except NameError: - return self._gen.next() + return next(self._gen) except StopIteration: return '' @@ -603,26 +654,6 @@ def captured_stderr(): return captured_output('stderr') -class cached_property(object): - """A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. - - Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 - """ - - def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') - self.func = func - - def __get__(self, obj, cls): - if obj is None: - # We're being accessed from the class itself, not from an object - return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - def get_installed_version(dist_name, working_set=None): """Get the installed version of dist_name avoiding pkg_resources cache""" # Create a requirement that we'll look for inside of setuptools. @@ -876,7 +907,7 @@ def is_console_interactive(): def hash_file(path, blocksize=1 << 20): - # type: (str, int) -> Tuple[Any, int] + # type: (Text, int) -> Tuple[Any, int] """Return (hash, length) for path using hashlib.sha256() """ @@ -911,3 +942,18 @@ def pairwise(iterable): """ iterable = iter(iterable) return zip_longest(iterable, iterable) + + +def partition( + pred, # type: Callable[[T], bool] + iterable, # type: Iterable[T] +): + # type: (...) -> Tuple[Iterable[T], Iterable[T]] + """ + Use a predicate to partition entries into false entries and true entries, + like + + partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + """ + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py index 29e1441..d1c2f22 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py @@ -10,6 +10,8 @@ class KeyBasedCompareMixin(object): """Provides comparison capabilities that is based on a key """ + __slots__ = ['_compare_key', '_defining_class'] + def __init__(self, key, defining_class): self._compare_key = key self._defining_class = defining_class diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/parallel.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/parallel.py new file mode 100644 index 0000000..9fe1fe8 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/parallel.py @@ -0,0 +1,107 @@ +"""Convenient parallelization of higher order functions. + +This module provides two helper functions, with appropriate fallbacks on +Python 2 and on systems lacking support for synchronization mechanisms: + +- map_multiprocess +- map_multithread + +These helpers work like Python 3's map, with two differences: + +- They don't guarantee the order of processing of + the elements of the iterable. +- The underlying process/thread pools chop the iterable into + a number of chunks, so that for very long iterables using + a large value for chunksize can make the job complete much faster + than using the default value of 1. +""" + +__all__ = ['map_multiprocess', 'map_multithread'] + +from contextlib import contextmanager +from multiprocessing import Pool as ProcessPool +from multiprocessing.dummy import Pool as ThreadPool + +from pip._vendor.requests.adapters import DEFAULT_POOLSIZE +from pip._vendor.six import PY2 +from pip._vendor.six.moves import map + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, Iterable, Iterator, Union, TypeVar + from multiprocessing import pool + + Pool = Union[pool.Pool, pool.ThreadPool] + S = TypeVar('S') + T = TypeVar('T') + +# On platforms without sem_open, multiprocessing[.dummy] Pool +# cannot be created. +try: + import multiprocessing.synchronize # noqa +except ImportError: + LACK_SEM_OPEN = True +else: + LACK_SEM_OPEN = False + +# Incredibly large timeout to work around bpo-8296 on Python 2. +TIMEOUT = 2000000 + + +@contextmanager +def closing(pool): + # type: (Pool) -> Iterator[Pool] + """Return a context manager making sure the pool closes properly.""" + try: + yield pool + finally: + # For Pool.imap*, close and join are needed + # for the returned iterator to begin yielding. + pool.close() + pool.join() + pool.terminate() + + +def _map_fallback(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Make an iterator applying func to each element in iterable. + + This function is the sequential fallback either on Python 2 + where Pool.imap* doesn't react to KeyboardInterrupt + or when sem_open is unavailable. + """ + return map(func, iterable) + + +def _map_multiprocess(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a process pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ProcessPool()) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +def _map_multithread(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a thread pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ThreadPool(DEFAULT_POOLSIZE)) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +if LACK_SEM_OPEN or PY2: + map_multiprocess = map_multithread = _map_fallback +else: + map_multiprocess = _map_multiprocess + map_multithread = _map_multithread diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py index 55c82da..d398e68 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import import logging @@ -188,6 +185,8 @@ def call_subprocess( stderr=subprocess.STDOUT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd, env=env, ) + assert proc.stdin + assert proc.stdout proc.stdin.close() except Exception as exc: if log_failed_cmd: @@ -208,6 +207,7 @@ def call_subprocess( log_subprocess(line) # Update the spinner. if use_spinner: + assert spinner spinner.spin() try: proc.wait() @@ -218,6 +218,7 @@ def call_subprocess( proc.returncode and proc.returncode not in extra_ok_returncodes ) if use_spinner: + assert spinner if proc_had_error: spinner.finish("error") else: @@ -241,8 +242,10 @@ def call_subprocess( raise InstallationError(exc_msg) elif on_returncode == 'warn': subprocess_logger.warning( - 'Command "{}" had error code {} in {}'.format( - command_desc, proc.returncode, cwd) + 'Command "%s" had error code %s in %s', + command_desc, + proc.returncode, + cwd, ) elif on_returncode == 'ignore': pass diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py index 201ba6d..03aa828 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py @@ -8,6 +8,7 @@ import tempfile from contextlib import contextmanager from pip._vendor.contextlib2 import ExitStack +from pip._vendor.six import ensure_text from pip._internal.utils.misc import enum, rmtree from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -184,7 +185,7 @@ class TempDirectory(object): path = os.path.realpath( tempfile.mkdtemp(prefix="pip-{}-".format(kind)) ) - logger.debug("Created temporary directory: {}".format(path)) + logger.debug("Created temporary directory: %s", path) return path def cleanup(self): @@ -193,7 +194,9 @@ class TempDirectory(object): """ self._deleted = True if os.path.exists(self._path): - rmtree(self._path) + # Make sure to pass unicode on Python 2 to make the contents also + # use unicode, ensuring non-ASCII names and can be represented. + rmtree(ensure_text(self._path)) class AdjacentTempDirectory(TempDirectory): @@ -267,5 +270,5 @@ class AdjacentTempDirectory(TempDirectory): tempfile.mkdtemp(prefix="pip-{}-".format(kind)) ) - logger.debug("Created temporary directory: {}".format(path)) + logger.debug("Created temporary directory: %s", path) return path diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py index 7252dc2..620f31e 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py @@ -1,10 +1,6 @@ """Utilities related archives. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -26,6 +22,7 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: from typing import Iterable, List, Optional, Text, Union + from zipfile import ZipInfo logger = logging.getLogger(__name__) @@ -48,6 +45,7 @@ except ImportError: def current_umask(): + # type: () -> int """Get the current umask which involves having to set it temporarily.""" mask = os.umask(0) os.umask(mask) @@ -98,6 +96,23 @@ def is_within_directory(directory, target): return prefix == abs_directory +def set_extracted_file_to_default_mode_plus_executable(path): + # type: (Union[str, Text]) -> None + """ + Make file present at path have execute for user/group/world + (chmod +x) is no-op on windows per python docs + """ + os.chmod(path, (0o777 & ~current_umask() | 0o111)) + + +def zip_item_is_executable(info): + # type: (ZipInfo) -> bool + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + return bool(mode and stat.S_ISREG(mode) and mode & 0o111) + + def unzip_file(filename, location, flatten=True): # type: (str, str, bool) -> None """ @@ -139,13 +154,8 @@ def unzip_file(filename, location, flatten=True): shutil.copyfileobj(fp, destfp) finally: fp.close() - mode = info.external_attr >> 16 - # if mode and regular file and any execute permissions for - # user/group/world? - if mode and stat.S_ISREG(mode) and mode & 0o111: - # make dest file have execute for user/group/world - # (chmod +x) no-op on windows per python docs - os.chmod(fn, (0o777 - current_umask() | 0o111)) + if zip_item_is_executable(info): + set_extracted_file_to_default_mode_plus_executable(fn) finally: zipfp.close() @@ -219,6 +229,7 @@ def untar_file(filename, location): ) continue ensure_dir(os.path.dirname(path)) + assert fp is not None with open(path, 'wb') as destfp: shutil.copyfileobj(fp, destfp) fp.close() @@ -227,9 +238,7 @@ def untar_file(filename, location): tar.utime(member, path) # type: ignore # member have any execute permissions for user/group/world? if member.mode & 0o111: - # make dest file have execute for user/group/world - # no-op on windows per python docs - os.chmod(path, (0o777 - current_umask() | 0o111)) + set_extracted_file_to_default_mode_plus_executable(path) finally: tar.close() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py index 596a69a..4a78128 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +import io import logging import os import re @@ -51,7 +52,9 @@ def _get_pyvenv_cfg_lines(): """ pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') try: - with open(pyvenv_cfg_file) as f: + # Although PEP 405 does not specify, the built-in venv module always + # writes with UTF-8. (pypa/pip#8717) + with io.open(pyvenv_cfg_file, encoding='utf-8') as f: return f.read().splitlines() # avoids trailing newlines except IOError: return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py index 3ebb771..9ce371c 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py @@ -121,7 +121,7 @@ def wheel_dist_info_dir(source, name): it doesn't match the provided name. """ # Zip file path separators must be / - subdirs = list(set(p.split("/")[0] for p in source.namelist())) + subdirs = set(p.split("/", 1)[0] for p in source.namelist()) info_dirs = [s for s in subdirs if s.endswith('.dist-info')] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py index 347c06f..94408c5 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py @@ -54,8 +54,7 @@ class Bazaar(VersionControl): url, rev_options = self.get_url_rev_options(url) self.run_command( - make_command('export', location, url, rev_options.to_args()), - show_stdout=False, + make_command('export', location, url, rev_options.to_args()) ) def fetch_new(self, dest, url, rev_options): @@ -92,7 +91,7 @@ class Bazaar(VersionControl): @classmethod def get_remote_url(cls, location): - urls = cls.run_command(['info'], show_stdout=False, cwd=location) + urls = cls.run_command(['info'], cwd=location) for line in urls.splitlines(): line = line.strip() for x in ('checkout of branch: ', @@ -107,7 +106,7 @@ class Bazaar(VersionControl): @classmethod def get_revision(cls, location): revision = cls.run_command( - ['revno'], show_stdout=False, cwd=location, + ['revno'], cwd=location, ) return revision.splitlines()[-1] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py index e173ec8..a9c7fb6 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py @@ -11,7 +11,7 @@ from pip._vendor.packaging.version import parse as parse_version from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib import request as urllib_request -from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.exceptions import BadCommand, SubProcessError from pip._internal.utils.misc import display_path, hide_url from pip._internal.utils.subprocess import make_command from pip._internal.utils.temp_dir import TempDirectory @@ -78,7 +78,7 @@ class Git(VersionControl): def get_git_version(self): VERSION_PFX = 'git version ' - version = self.run_command(['version'], show_stdout=False) + version = self.run_command(['version']) if version.startswith(VERSION_PFX): version = version[len(VERSION_PFX):].split()[0] else: @@ -101,7 +101,7 @@ class Git(VersionControl): # and to suppress the message to stderr. args = ['symbolic-ref', '-q', 'HEAD'] output = cls.run_command( - args, extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + args, extra_ok_returncodes=(1, ), cwd=location, ) ref = output.strip() @@ -120,7 +120,7 @@ class Git(VersionControl): self.unpack(temp_dir.path, url=url) self.run_command( ['checkout-index', '-a', '-f', '--prefix', location], - show_stdout=False, cwd=temp_dir.path + cwd=temp_dir.path ) @classmethod @@ -134,8 +134,13 @@ class Git(VersionControl): rev: the revision name. """ # Pass rev to pre-filter the list. - output = cls.run_command(['show-ref', rev], cwd=dest, - show_stdout=False, on_returncode='ignore') + + output = '' + try: + output = cls.run_command(['show-ref', rev], cwd=dest) + except SubProcessError: + pass + refs = {} for line in output.strip().splitlines(): try: @@ -286,7 +291,7 @@ class Git(VersionControl): # exits with return code 1 if there are no matching lines. stdout = cls.run_command( ['config', '--get-regexp', r'remote\..*\.url'], - extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + extra_ok_returncodes=(1, ), cwd=location, ) remotes = stdout.splitlines() try: @@ -306,7 +311,7 @@ class Git(VersionControl): if rev is None: rev = 'HEAD' current_rev = cls.run_command( - ['rev-parse', rev], show_stdout=False, cwd=location, + ['rev-parse', rev], cwd=location, ) return current_rev.strip() @@ -319,7 +324,7 @@ class Git(VersionControl): # find the repo root git_dir = cls.run_command( ['rev-parse', '--git-dir'], - show_stdout=False, cwd=location).strip() + cwd=location).strip() if not os.path.isabs(git_dir): git_dir = os.path.join(location, git_dir) repo_root = os.path.abspath(os.path.join(git_dir, '..')) @@ -378,15 +383,13 @@ class Git(VersionControl): r = cls.run_command( ['rev-parse', '--show-toplevel'], cwd=location, - show_stdout=False, - on_returncode='raise', log_failed_cmd=False, ) except BadCommand: logger.debug("could not determine if %s is under git control " "because git is not available", location) return None - except InstallationError: + except SubProcessError: return None return os.path.normpath(r.rstrip('\r\n')) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py index 75e903c..69763fe 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py @@ -8,7 +8,7 @@ import os from pip._vendor.six.moves import configparser -from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.exceptions import BadCommand, SubProcessError from pip._internal.utils.misc import display_path from pip._internal.utils.subprocess import make_command from pip._internal.utils.temp_dir import TempDirectory @@ -47,7 +47,7 @@ class Mercurial(VersionControl): self.unpack(temp_dir.path, url=url) self.run_command( - ['archive', location], show_stdout=False, cwd=temp_dir.path + ['archive', location], cwd=temp_dir.path ) def fetch_new(self, dest, url, rev_options): @@ -92,7 +92,7 @@ class Mercurial(VersionControl): def get_remote_url(cls, location): url = cls.run_command( ['showconfig', 'paths.default'], - show_stdout=False, cwd=location).strip() + cwd=location).strip() if cls._is_local_repository(url): url = path_to_url(url) return url.strip() @@ -103,8 +103,7 @@ class Mercurial(VersionControl): Return the repository-local changeset revision number, as an integer. """ current_revision = cls.run_command( - ['parents', '--template={rev}'], - show_stdout=False, cwd=location).strip() + ['parents', '--template={rev}'], cwd=location).strip() return current_revision @classmethod @@ -115,7 +114,7 @@ class Mercurial(VersionControl): """ current_rev_hash = cls.run_command( ['parents', '--template={node}'], - show_stdout=False, cwd=location).strip() + cwd=location).strip() return current_rev_hash @classmethod @@ -131,7 +130,7 @@ class Mercurial(VersionControl): """ # find the repo root repo_root = cls.run_command( - ['root'], show_stdout=False, cwd=location).strip() + ['root'], cwd=location).strip() if not os.path.isabs(repo_root): repo_root = os.path.abspath(os.path.join(location, repo_root)) return find_path_to_setup_from_repo_root(location, repo_root) @@ -145,15 +144,13 @@ class Mercurial(VersionControl): r = cls.run_command( ['root'], cwd=location, - show_stdout=False, - on_returncode='raise', log_failed_cmd=False, ) except BadCommand: logger.debug("could not determine if %s is under hg control " "because hg is not available", location) return None - except InstallationError: + except SubProcessError: return None return os.path.normpath(r.rstrip('\r\n')) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py index 0ec6597..ab13497 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py @@ -56,7 +56,7 @@ class Subversion(VersionControl): # Note: taken from setuptools.command.egg_info revision = 0 - for base, dirs, files in os.walk(location): + for base, dirs, _ in os.walk(location): if cls.dirname not in dirs: dirs[:] = [] continue # no sense walking uncontrolled subdirs @@ -132,7 +132,7 @@ class Subversion(VersionControl): @classmethod def _get_svn_url_rev(cls, location): - from pip._internal.exceptions import InstallationError + from pip._internal.exceptions import SubProcessError entries_path = os.path.join(location, cls.dirname, 'entries') if os.path.exists(entries_path): @@ -165,13 +165,12 @@ class Subversion(VersionControl): # are only potentially needed for remote server requests. xml = cls.run_command( ['info', '--xml', location], - show_stdout=False, ) url = _svn_info_xml_url_re.search(xml).group(1) revs = [ int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) ] - except InstallationError: + except SubProcessError: url, revs = None, [] if revs: @@ -214,13 +213,16 @@ class Subversion(VersionControl): # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 # svn, version 1.7.14 (r1542130) # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu + # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) + # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 version_prefix = 'svn, version ' - version = self.run_command(['--version'], show_stdout=False) + version = self.run_command(['--version']) + if not version.startswith(version_prefix): return () version = version[len(version_prefix):].split()[0] - version_list = version.split('.') + version_list = version.partition('-')[0].split('.') try: parsed_version = tuple(map(int, version_list)) except ValueError: @@ -297,7 +299,7 @@ class Subversion(VersionControl): 'export', self.get_remote_call_options(), rev_options.to_args(), url, location, ) - self.run_command(cmd_args, show_stdout=False) + self.run_command(cmd_args) def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py index 71b4650..96f830f 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py @@ -6,13 +6,19 @@ import errno import logging import os import shutil +import subprocess import sys from pip._vendor import pkg_resources from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.compat import samefile +from pip._internal.exceptions import ( + BadCommand, + InstallationError, + SubProcessError, +) +from pip._internal.utils.compat import console_to_str, samefile +from pip._internal.utils.logging import subprocess_logger from pip._internal.utils.misc import ( ask_path_exists, backup_dir, @@ -21,16 +27,20 @@ from pip._internal.utils.misc import ( hide_value, rmtree, ) -from pip._internal.utils.subprocess import call_subprocess, make_command +from pip._internal.utils.subprocess import ( + format_command_args, + make_command, + make_subprocess_output_error, + reveal_command_args, +) from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import get_url_scheme if MYPY_CHECK_RUNNING: from typing import ( - Any, Dict, Iterable, Iterator, List, Mapping, Optional, Text, Tuple, - Type, Union + Dict, Iterable, Iterator, List, Optional, Text, Tuple, + Type, Union, Mapping, Any ) - from pip._internal.cli.spinners import SpinnerInterface from pip._internal.utils.misc import HiddenText from pip._internal.utils.subprocess import CommandArgs @@ -71,6 +81,92 @@ def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): return req +def call_subprocess( + cmd, # type: Union[List[str], CommandArgs] + cwd=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + log_failed_cmd=True # type: Optional[bool] +): + # type: (...) -> Text + """ + Args: + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + log_failed_cmd: if false, failed commands are not logged, + only raised. + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + + # log the subprocess output at DEBUG level. + log_subprocess = subprocess_logger.debug + + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + + # Whether the subprocess will be visible in the console. + showing_subprocess = True + + command_desc = format_command_args(cmd) + try: + proc = subprocess.Popen( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=cwd + ) + if proc.stdin: + proc.stdin.close() + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + while True: + # The "line" value is a unicode string in Python 2. + line = None + if proc.stdout: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + + # Show the line immediately. + log_subprocess(line) + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + + proc_had_error = ( + proc.returncode and proc.returncode not in extra_ok_returncodes + ) + if proc_had_error: + if not showing_subprocess and log_failed_cmd: + # Then the subprocess streams haven't been logged to the + # console yet. + msg = make_subprocess_output_error( + cmd_args=cmd, + cwd=cwd, + lines=all_output, + exit_status=proc.returncode, + ) + subprocess_logger.error(msg) + exc_msg = ( + 'Command errored out with exit status {}: {} ' + 'Check the logs for full command output.' + ).format(proc.returncode, command_desc) + raise SubProcessError(exc_msg) + return ''.join(all_output) + + def find_path_to_setup_from_repo_root(location, repo_root): # type: (str, str) -> Optional[str] """ @@ -659,13 +755,9 @@ class VersionControl(object): def run_command( cls, cmd, # type: Union[List[str], CommandArgs] - show_stdout=True, # type: bool cwd=None, # type: Optional[str] - on_returncode='raise', # type: str - extra_ok_returncodes=None, # type: Optional[Iterable[int]] - command_desc=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] - spinner=None, # type: Optional[SpinnerInterface] + extra_ok_returncodes=None, # type: Optional[Iterable[int]] log_failed_cmd=True # type: bool ): # type: (...) -> Text @@ -676,13 +768,9 @@ class VersionControl(object): """ cmd = make_command(cls.name, *cmd) try: - return call_subprocess(cmd, show_stdout, cwd, - on_returncode=on_returncode, - extra_ok_returncodes=extra_ok_returncodes, - command_desc=command_desc, + return call_subprocess(cmd, cwd, extra_environ=extra_environ, - unset_environ=cls.unset_environ, - spinner=spinner, + extra_ok_returncodes=extra_ok_returncodes, log_failed_cmd=log_failed_cmd) except OSError as e: # errno.ENOENT = no such file or directory diff --git a/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py b/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py index fcaeeb6..fa08016 100644 --- a/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py +++ b/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py @@ -1,9 +1,6 @@ """Orchestrator for building wheels from InstallRequirements. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import logging import os.path import re @@ -23,7 +20,7 @@ from pip._internal.vcs import vcs if MYPY_CHECK_RUNNING: from typing import ( - Any, Callable, Iterable, List, Optional, Pattern, Tuple, + Any, Callable, Iterable, List, Optional, Tuple, ) from pip._internal.cache import WheelCache @@ -34,10 +31,11 @@ if MYPY_CHECK_RUNNING: logger = logging.getLogger(__name__) +_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.IGNORECASE) -def _contains_egg_info( - s, _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): - # type: (str, Pattern[str]) -> bool + +def _contains_egg_info(s): + # type: (str) -> bool """Determine whether the string looks like an egg_info. :param s: The string to parse. E.g. foo-2.1 @@ -82,7 +80,7 @@ def _should_build( if not req.use_pep517 and not is_wheel_installed(): # we don't build legacy requirements if wheel is not installed logger.info( - "Using legacy setup.py install for %s, " + "Using legacy 'setup.py install' for %s, " "since package 'wheel' is not installed.", req.name, ) return False @@ -118,11 +116,8 @@ def _should_cache( wheel cache, assuming the wheel cache is available, and _should_build() has determined a wheel needs to be built. """ - if not should_build_for_install_command( - req, check_binary_allowed=_always_true - ): - # never cache if pip install would not have built - # (editable mode, etc) + if req.editable or not req.source_dir: + # never cache editable requirements return False if req.link and req.link.is_vcs: @@ -136,6 +131,7 @@ def _should_cache( return True return False + assert req.link base, ext = req.link.splitext() if _contains_egg_info(base): return True @@ -153,6 +149,7 @@ def _get_cache_dir( wheel need to be stored. """ cache_available = bool(wheel_cache.cache_dir) + assert req.link if cache_available and _should_cache(req): cache_dir = wheel_cache.get_path_for_link(req.link) else: @@ -200,7 +197,9 @@ def _build_one_inside_env( ): # type: (...) -> Optional[str] with TempDirectory(kind="wheel") as temp_dir: + assert req.name if req.use_pep517: + assert req.metadata_directory wheel_path = build_wheel_pep517( name=req.name, backend=req.pep517_backend, @@ -275,7 +274,7 @@ def build( # Build the wheels. logger.info( 'Building wheels for collected packages: %s', - ', '.join(req.name for req in requirements), + ', '.join(req.name for req in requirements), # type: ignore ) with indent_log(): @@ -298,12 +297,12 @@ def build( if build_successes: logger.info( 'Successfully built %s', - ' '.join([req.name for req in build_successes]), + ' '.join([req.name for req in build_successes]), # type: ignore ) if build_failures: logger.info( 'Failed to build %s', - ' '.join([req.name for req in build_failures]), + ' '.join([req.name for req in build_failures]), # type: ignore ) # Return a list of requirements that failed to build return build_successes, build_failures diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py index c3db83f..581db54 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py @@ -32,15 +32,11 @@ def vendored(modulename): try: __import__(modulename, globals(), locals(), level=0) except ImportError: - # We can just silently allow import failures to pass here. If we - # got to this point it means that ``import pip._vendor.whatever`` - # failed and so did ``import whatever``. Since we're importing this - # upfront in an attempt to alias imports, not erroring here will - # just mean we get a regular import error whenever pip *actually* - # tries to import one of these modules to use it, which actually - # gives us a better error message than we would have otherwise - # gotten. - pass + # This error used to be silenced in earlier variants of this file, to instead + # raise the error when pip actually tries to use the missing module. + # Based on inputs in #5354, this was changed to explicitly raise the error. + # Re-raising the exception without modifying it is an intentional choice. + raise else: sys.modules[vendored_name] = sys.modules[modulename] base, head = vendored_name.rsplit(".", 1) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py b/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py index 8bd9c9c..33a3b77 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py @@ -13,8 +13,8 @@ See for details and usage. # - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html # - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html -__version_info__ = (1, 4, 3) -__version__ = '.'.join(map(str, __version_info__)) +__version__ = "1.4.4" +__version_info__ = tuple(int(segment) for segment in __version__.split(".")) import sys diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__init__.py index 1e2dfac..5d52a62 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__init__.py @@ -1,3 +1,3 @@ from .core import contents, where -__version__ = "2020.04.05.1" +__version__ = "2020.06.20" diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/cacert.pem b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/cacert.pem index ece147c..0fd855f 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/cacert.pem +++ b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/cacert.pem @@ -58,38 +58,6 @@ AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== -----END CERTIFICATE----- -# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only -# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only -# Label: "Verisign Class 3 Public Primary Certification Authority - G3" -# Serial: 206684696279472310254277870180966723415 -# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 -# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 -# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b -N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t -KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu -kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm -CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ -Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu -imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te -2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe -DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC -/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p -F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt -TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== ------END CERTIFICATE----- - # Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Label: "Entrust.net Premium 2048 Secure Server CA" @@ -152,39 +120,6 @@ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Label: "AddTrust External Root" -# Serial: 1 -# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f -# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 -# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- - # Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. # Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. # Label: "Entrust Root Certification Authority" @@ -1499,47 +1434,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- -# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden -# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden -# Label: "Staat der Nederlanden Root CA - G2" -# Serial: 10000012 -# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a -# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 -# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f ------BEGIN CERTIFICATE----- -MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX -DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 -qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp -uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU -Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE -pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp -5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M -UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN -GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy -5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv -6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK -eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 -B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ -BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov -L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG -SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS -CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen -5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 -IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK -gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL -+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL -vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm -bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk -N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC -Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z -ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== ------END CERTIFICATE----- - # Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post # Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post # Label: "Hongkong Post Root CA 1" @@ -3788,47 +3682,6 @@ CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW 1KyLa2tJElMzrdfkviT8tQp21KW8EA== -----END CERTIFICATE----- -# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A. -# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A. -# Label: "LuxTrust Global Root 2" -# Serial: 59914338225734147123941058376788110305822489521 -# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c -# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f -# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5 ------BEGIN CERTIFICATE----- -MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL -BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV -BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw -MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B -LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F -ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem -hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 -EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn -Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 -zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ -96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m -j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g -DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ -8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j -X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH -hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB -KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 -Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT -+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL -BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 -BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO -jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 -loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c -qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ -2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ -JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre -zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf -LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ -x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 -oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr ------END CERTIFICATE----- - # Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM # Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM # Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" @@ -4639,3 +4492,129 @@ IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk 5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== -----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/core.py b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/core.py index 56b52a3..8987449 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/certifi/core.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/certifi/core.py @@ -9,7 +9,36 @@ This module returns the installation location of cacert.pem or its contents. import os try: - from importlib.resources import read_text + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where(): + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("pip._vendor.certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + + except ImportError: # This fallback will work for Python versions prior to 3.7 that lack the # importlib.resources module but relies on the existing `where` function @@ -19,11 +48,12 @@ except ImportError: with open(where(), "r", encoding=encoding) as data: return data.read() + # If we don't have importlib.resources, then we will just do the old logic + # of assuming we're on the filesystem and munge the path directly. + def where(): + f = os.path.dirname(__file__) -def where(): - f = os.path.dirname(__file__) - - return os.path.join(f, "cacert.pem") + return os.path.join(f, "cacert.pem") def contents(): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py index e19aebd..63d916e 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py @@ -6,7 +6,7 @@ # import logging -__version__ = '0.3.0' +__version__ = '0.3.1' class DistlibException(Exception): pass diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py index 159e49e..10ed362 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -14,7 +14,10 @@ import sys import stat from os.path import abspath import fnmatch -import collections +try: + from collections.abc import Callable +except ImportError: + from collections import Callable import errno from . import tarfile @@ -528,7 +531,7 @@ def register_archive_format(name, function, extra_args=None, description=''): """ if extra_args is None: extra_args = [] - if not isinstance(function, collections.Callable): + if not isinstance(function, Callable): raise TypeError('The %s object is not callable' % function) if not isinstance(extra_args, (tuple, list)): raise TypeError('extra_args needs to be a sequence') @@ -621,7 +624,7 @@ def _check_unpack_options(extensions, function, extra_args): raise RegistryError(msg % (extension, existing_extensions[extension])) - if not isinstance(function, collections.Callable): + if not isinstance(function, Callable): raise TypeError('The registered function must be a callable') diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py index ff328c8..c316fd9 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py @@ -319,7 +319,7 @@ except ImportError: # pragma: no cover try: callable = callable except NameError: # pragma: no cover - from collections import Callable + from collections.abc import Callable def callable(obj): return isinstance(obj, Callable) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py index c16c0c8..0a90c30 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py @@ -550,7 +550,7 @@ class InstalledDistribution(BaseInstalledDistribution): r = finder.find(WHEEL_METADATA_FILENAME) # Temporary - for legacy support if r is None: - r = finder.find('METADATA') + r = finder.find(LEGACY_METADATA_FILENAME) if r is None: raise ValueError('no %s found in %s' % (METADATA_FILENAME, path)) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py index 2d61378..6d5e236 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py @@ -5,7 +5,7 @@ # """Implementation of the Metadata for Python packages PEPs. -Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and withdrawn 2.0). """ from __future__ import unicode_literals @@ -194,38 +194,12 @@ def _best_version(fields): return '2.0' +# This follows the rules about transforming keys as described in +# https://www.python.org/dev/peps/pep-0566/#id17 _ATTR2FIELD = { - 'metadata_version': 'Metadata-Version', - 'name': 'Name', - 'version': 'Version', - 'platform': 'Platform', - 'supported_platform': 'Supported-Platform', - 'summary': 'Summary', - 'description': 'Description', - 'keywords': 'Keywords', - 'home_page': 'Home-page', - 'author': 'Author', - 'author_email': 'Author-email', - 'maintainer': 'Maintainer', - 'maintainer_email': 'Maintainer-email', - 'license': 'License', - 'classifier': 'Classifier', - 'download_url': 'Download-URL', - 'obsoletes_dist': 'Obsoletes-Dist', - 'provides_dist': 'Provides-Dist', - 'requires_dist': 'Requires-Dist', - 'setup_requires_dist': 'Setup-Requires-Dist', - 'requires_python': 'Requires-Python', - 'requires_external': 'Requires-External', - 'requires': 'Requires', - 'provides': 'Provides', - 'obsoletes': 'Obsoletes', - 'project_url': 'Project-URL', - 'private_version': 'Private-Version', - 'obsoleted_by': 'Obsoleted-By', - 'extension': 'Extension', - 'provides_extra': 'Provides-Extra', + name.lower().replace("-", "_"): name for name in _ALL_FIELDS } +_FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()} _PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') _VERSIONS_FIELDS = ('Requires-Python',) @@ -262,7 +236,7 @@ def _get_name_and_version(name, version, for_filename=False): class LegacyMetadata(object): """The legacy metadata of a release. - Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can instantiate the class with one of these arguments (or none): - *path*, the path to a metadata file - *fileobj* give a file-like object with metadata as content @@ -381,6 +355,11 @@ class LegacyMetadata(object): value = msg[field] if value is not None and value != 'UNKNOWN': self.set(field, value) + + # PEP 566 specifies that the body be used for the description, if + # available + body = msg.get_payload() + self["Description"] = body if body else self["Description"] # logger.debug('Attempting to set metadata for %s', self) # self.set_metadata_version() @@ -567,57 +546,21 @@ class LegacyMetadata(object): Field names will be converted to use the underscore-lowercase style instead of hyphen-mixed case (i.e. home_page instead of Home-page). + This is as per https://www.python.org/dev/peps/pep-0566/#id17. """ self.set_metadata_version() - mapping_1_0 = ( - ('metadata_version', 'Metadata-Version'), - ('name', 'Name'), - ('version', 'Version'), - ('summary', 'Summary'), - ('home_page', 'Home-page'), - ('author', 'Author'), - ('author_email', 'Author-email'), - ('license', 'License'), - ('description', 'Description'), - ('keywords', 'Keywords'), - ('platform', 'Platform'), - ('classifiers', 'Classifier'), - ('download_url', 'Download-URL'), - ) + fields = _version2fieldlist(self['Metadata-Version']) data = {} - for key, field_name in mapping_1_0: + + for field_name in fields: if not skip_missing or field_name in self._fields: - data[key] = self[field_name] - - if self['Metadata-Version'] == '1.2': - mapping_1_2 = ( - ('requires_dist', 'Requires-Dist'), - ('requires_python', 'Requires-Python'), - ('requires_external', 'Requires-External'), - ('provides_dist', 'Provides-Dist'), - ('obsoletes_dist', 'Obsoletes-Dist'), - ('project_url', 'Project-URL'), - ('maintainer', 'Maintainer'), - ('maintainer_email', 'Maintainer-email'), - ) - for key, field_name in mapping_1_2: - if not skip_missing or field_name in self._fields: - if key != 'project_url': - data[key] = self[field_name] - else: - data[key] = [','.join(u) for u in self[field_name]] - - elif self['Metadata-Version'] == '1.1': - mapping_1_1 = ( - ('provides', 'Provides'), - ('requires', 'Requires'), - ('obsoletes', 'Obsoletes'), - ) - for key, field_name in mapping_1_1: - if not skip_missing or field_name in self._fields: + key = _FIELD2ATTR[field_name] + if key != 'project_url': data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] return data @@ -1003,10 +946,14 @@ class Metadata(object): LEGACY_MAPPING = { 'name': 'Name', 'version': 'Version', - 'license': 'License', + ('extensions', 'python.details', 'license'): 'License', 'summary': 'Summary', 'description': 'Description', - 'classifiers': 'Classifier', + ('extensions', 'python.project', 'project_urls', 'Home'): 'Home-page', + ('extensions', 'python.project', 'contacts', 0, 'name'): 'Author', + ('extensions', 'python.project', 'contacts', 0, 'email'): 'Author-email', + 'source_url': 'Download-URL', + ('extensions', 'python.details', 'classifiers'): 'Classifier', } def _to_legacy(self): @@ -1034,16 +981,29 @@ class Metadata(object): assert self._data and not self._legacy result = LegacyMetadata() nmd = self._data + # import pdb; pdb.set_trace() for nk, ok in self.LEGACY_MAPPING.items(): - if nk in nmd: - result[ok] = nmd[nk] + if not isinstance(nk, tuple): + if nk in nmd: + result[ok] = nmd[nk] + else: + d = nmd + found = True + for k in nk: + try: + d = d[k] + except (KeyError, IndexError): + found = False + break + if found: + result[ok] = d r1 = process_entries(self.run_requires + self.meta_requires) r2 = process_entries(self.build_requires + self.dev_requires) if self.extras: result['Provides-Extra'] = sorted(self.extras) result['Requires-Dist'] = sorted(r1) result['Setup-Requires-Dist'] = sorted(r2) - # TODO: other fields such as contacts + # TODO: any other fields wanted return result def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py index 5185974..03f8f21 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py @@ -48,7 +48,7 @@ if __name__ == '__main__': ''' -def _enquote_executable(executable): +def enquote_executable(executable): if ' ' in executable: # make sure we quote only the executable in case of env # for example /usr/bin/env "/dir with spaces/bin/jython" @@ -63,6 +63,8 @@ def _enquote_executable(executable): executable = '"%s"' % executable return executable +# Keep the old name around (for now), as there is at least one project using it! +_enquote_executable = enquote_executable class ScriptMaker(object): """ @@ -88,6 +90,7 @@ class ScriptMaker(object): self._is_nt = os.name == 'nt' or ( os.name == 'java' and os._name == 'nt') + self.version_info = sys.version_info def _get_alternate_executable(self, executable, options): if options.get('gui', False) and self._is_nt: # pragma: no cover @@ -185,7 +188,7 @@ class ScriptMaker(object): # If the user didn't specify an executable, it may be necessary to # cater for executable paths with spaces (not uncommon on Windows) if enquote: - executable = _enquote_executable(executable) + executable = enquote_executable(executable) # Issue #51: don't use fsencode, since we later try to # check that the shebang is decodable using utf-8. executable = executable.encode('utf-8') @@ -293,10 +296,10 @@ class ScriptMaker(object): if '' in self.variants: scriptnames.add(name) if 'X' in self.variants: - scriptnames.add('%s%s' % (name, sys.version_info[0])) + scriptnames.add('%s%s' % (name, self.version_info[0])) if 'X.Y' in self.variants: - scriptnames.add('%s-%s.%s' % (name, sys.version_info[0], - sys.version_info[1])) + scriptnames.add('%s-%s.%s' % (name, self.version_info[0], + self.version_info[1])) if options and options.get('gui', False): ext = 'pyw' else: diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py index bd17938..1e2c7a0 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py @@ -26,7 +26,8 @@ import zipfile from . import __version__, DistlibException from .compat import sysconfig, ZipFile, fsdecode, text_type, filter from .database import InstalledDistribution -from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, cached_property, get_cache_base, read_exports, tempdir) from .version import NormalizedVersion, UnsupportedVersionError @@ -221,10 +222,12 @@ class Wheel(object): wheel_metadata = self.get_wheel_metadata(zf) wv = wheel_metadata['Wheel-Version'].split('.', 1) file_version = tuple([int(i) for i in wv]) - if file_version < (1, 1): - fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, 'METADATA'] - else: - fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + # if file_version < (1, 1): + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, + # LEGACY_METADATA_FILENAME] + # else: + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] result = None for fn in fns: try: @@ -299,10 +302,9 @@ class Wheel(object): return hash_kind, result def write_record(self, records, record_path, base): - records = list(records) # make a copy for sorting + records = list(records) # make a copy, as mutated p = to_posix(os.path.relpath(record_path, base)) records.append((p, '', '')) - records.sort() with CSVWriter(record_path) as writer: for row in records: writer.writerow(row) @@ -425,6 +427,18 @@ class Wheel(object): ap = to_posix(os.path.join(info_dir, 'WHEEL')) archive_paths.append((ap, p)) + # sort the entries by archive path. Not needed by any spec, but it + # keeps the archive listing and RECORD tidier than they would otherwise + # be. Use the number of path segments to keep directory entries together, + # and keep the dist-info stuff at the end. + def sorter(t): + ap = t[0] + n = ap.count('/') + if '.dist-info' in ap: + n += 10000 + return (n, ap) + archive_paths = sorted(archive_paths, key=sorter) + # Now, at last, RECORD. # Paths in here are archive paths - nothing else makes sense. self.write_records((distinfo, info_dir), libdir, archive_paths) @@ -476,7 +490,7 @@ class Wheel(object): data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver - metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') record_name = posixpath.join(info_dir, 'RECORD') @@ -619,7 +633,7 @@ class Wheel(object): for v in epdata[k].values(): s = '%s:%s' % (v.prefix, v.suffix) if v.flags: - s += ' %s' % v.flags + s += ' [%s]' % ','.join(v.flags) d[v.name] = s except Exception: logger.warning('Unable to read legacy script ' @@ -773,7 +787,7 @@ class Wheel(object): data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver - metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') record_name = posixpath.join(info_dir, 'RECORD') @@ -842,7 +856,7 @@ class Wheel(object): def get_version(path_map, info_dir): version = path = None - key = '%s/%s' % (info_dir, METADATA_FILENAME) + key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) if key not in path_map: key = '%s/PKG-INFO' % info_dir if key in path_map: @@ -868,7 +882,7 @@ class Wheel(object): if updated: md = Metadata(path=path) md.version = updated - legacy = not path.endswith(METADATA_FILENAME) + legacy = path.endswith(LEGACY_METADATA_FILENAME) md.write(path=path, legacy=legacy) logger.debug('Version updated from %r to %r', version, updated) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__init__.py index 0491234..d1d82f1 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__init__.py @@ -32,4 +32,4 @@ __all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", # this has to be at the top level, see how setup.py parses this #: Distribution version number. -__version__ = "1.0.1" +__version__ = "1.1" diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_ihatexml.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_ihatexml.py index 4c77717..3ff803c 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_ihatexml.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -136,6 +136,7 @@ def normaliseCharList(charList): i += j return rv + # We don't really support characters above the BMP :( max_unicode = int("FFFF", 16) @@ -254,7 +255,7 @@ class InfosetFilter(object): nameRest = name[1:] m = nonXmlNameFirstBMPRegexp.match(nameFirst) if m: - warnings.warn("Coercing non-XML name", DataLossWarning) + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) nameFirstOutput = self.getReplacementCharacter(nameFirst) else: nameFirstOutput = nameFirst @@ -262,7 +263,7 @@ class InfosetFilter(object): nameRestOutput = nameRest replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) for char in replaceChars: - warnings.warn("Coercing non-XML name", DataLossWarning) + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) replacement = self.getReplacementCharacter(char) nameRestOutput = nameRestOutput.replace(char, replacement) return nameFirstOutput + nameRestOutput diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_inputstream.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_inputstream.py index a65e55f..e0bb376 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_inputstream.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -1,10 +1,11 @@ from __future__ import absolute_import, division, unicode_literals -from pip._vendor.six import text_type, binary_type +from pip._vendor.six import text_type from pip._vendor.six.moves import http_client, urllib import codecs import re +from io import BytesIO, StringIO from pip._vendor import webencodings @@ -12,13 +13,6 @@ from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase from .constants import _ReparseException from . import _utils -from io import StringIO - -try: - from io import BytesIO -except ImportError: - BytesIO = StringIO - # Non-unicode versions of constants for use in the pre-parser spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) @@ -40,13 +34,13 @@ if _utils.supports_lone_surrogates: else: invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) -non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, - 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, - 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, - 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, - 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, - 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, - 0x10FFFE, 0x10FFFF]) +non_bmp_invalid_codepoints = {0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF} ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") @@ -367,7 +361,7 @@ class HTMLUnicodeInputStream(object): def unget(self, char): # Only one character is allowed to be ungotten at once - it must # be consumed again before any further call to unget - if char is not None: + if char is not EOF: if self.chunkOffset == 0: # unget is called quite rarely, so it's a good idea to do # more work here if it saves a bit of work in the frequently @@ -449,7 +443,7 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): try: stream.seek(stream.tell()) - except: # pylint:disable=bare-except + except Exception: stream = BufferedStream(stream) return stream @@ -461,7 +455,7 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): if charEncoding[0] is not None: return charEncoding - # If we've been overriden, we've been overriden + # If we've been overridden, we've been overridden charEncoding = lookupEncoding(self.override_encoding), "certain" if charEncoding[0] is not None: return charEncoding @@ -664,9 +658,7 @@ class EncodingBytes(bytes): """Look for a sequence of bytes at the start of a string. If the bytes are found return True and advance the position to the byte after the match. Otherwise return False and leave the position alone""" - p = self.position - data = self[p:p + len(bytes)] - rv = data.startswith(bytes) + rv = self.startswith(bytes, self.position) if rv: self.position += len(bytes) return rv @@ -674,15 +666,11 @@ class EncodingBytes(bytes): def jumpTo(self, bytes): """Look for the next sequence of bytes matching a given sequence. If a match is found advance the position to the last byte of the match""" - newPosition = self[self.position:].find(bytes) - if newPosition > -1: - # XXX: This is ugly, but I can't see a nicer way to fix this. - if self._position == -1: - self._position = 0 - self._position += (newPosition + len(bytes) - 1) - return True - else: + try: + self._position = self.index(bytes, self.position) + len(bytes) - 1 + except ValueError: raise StopIteration + return True class EncodingParser(object): @@ -694,6 +682,9 @@ class EncodingParser(object): self.encoding = None def getEncoding(self): + if b"= (3, 7): + attributeMap = dict +else: + attributeMap = OrderedDict + class HTMLTokenizer(object): """ This class takes care of tokenizing HTML. @@ -228,6 +234,14 @@ class HTMLTokenizer(object): # Add token to the queue to be yielded if (token["type"] in tagTokenTypes): token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + data = attributeMap(raw) + if len(raw) > len(data): + # we had some duplicated attribute, fix so first wins + data.update(raw[::-1]) + token["data"] = data + if token["type"] == tokenTypes["EndTag"]: if token["data"]: self.tokenQueue.append({"type": tokenTypes["ParseError"], diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__init__.py index a5ba4bf..07bad5d 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -1,14 +1,5 @@ from __future__ import absolute_import, division, unicode_literals -from .py import Trie as PyTrie +from .py import Trie -Trie = PyTrie - -# pylint:disable=wrong-import-position -try: - from .datrie import Trie as DATrie -except ImportError: - pass -else: - Trie = DATrie -# pylint:enable=wrong-import-position +__all__ = ["Trie"] diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/datrie.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/datrie.py deleted file mode 100644 index e2e5f86..0000000 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/datrie.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals - -from datrie import Trie as DATrie -from pip._vendor.six import text_type - -from ._base import Trie as ABCTrie - - -class Trie(ABCTrie): - def __init__(self, data): - chars = set() - for key in data.keys(): - if not isinstance(key, text_type): - raise TypeError("All keys must be strings") - for char in key: - chars.add(char) - - self._data = DATrie("".join(chars)) - for key, value in data.items(): - self._data[key] = value - - def __contains__(self, key): - return key in self._data - - def __len__(self): - return len(self._data) - - def __iter__(self): - raise NotImplementedError() - - def __getitem__(self, key): - return self._data[key] - - def keys(self, prefix=None): - return self._data.keys(prefix) - - def has_keys_with_prefix(self, prefix): - return self._data.has_keys_with_prefix(prefix) - - def longest_prefix(self, prefix): - return self._data.longest_prefix(prefix) - - def longest_prefix_item(self, prefix): - return self._data.longest_prefix_item(prefix) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_utils.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_utils.py index 0703afb..d7c4926 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_utils.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_utils.py @@ -2,12 +2,20 @@ from __future__ import absolute_import, division, unicode_literals from types import ModuleType -from pip._vendor.six import text_type - try: - import xml.etree.cElementTree as default_etree + from collections.abc import Mapping except ImportError: + from collections import Mapping + +from pip._vendor.six import text_type, PY3 + +if PY3: import xml.etree.ElementTree as default_etree +else: + try: + import xml.etree.cElementTree as default_etree + except ImportError: + import xml.etree.ElementTree as default_etree __all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", @@ -27,7 +35,7 @@ try: # We need this with u"" because of http://bugs.jython.org/issue2039 _x = eval('u"\\uD800"') # pylint:disable=eval-used assert isinstance(_x, text_type) -except: # pylint:disable=bare-except +except Exception: supports_lone_surrogates = False else: supports_lone_surrogates = True @@ -47,9 +55,6 @@ class MethodDispatcher(dict): """ def __init__(self, items=()): - # Using _dictEntries instead of directly assigning to self is about - # twice as fast. Please do careful performance testing before changing - # anything here. _dictEntries = [] for name, value in items: if isinstance(name, (list, tuple, frozenset, set)): @@ -64,6 +69,36 @@ class MethodDispatcher(dict): def __getitem__(self, key): return dict.get(self, key, self.default) + def __get__(self, instance, owner=None): + return BoundMethodDispatcher(instance, self) + + +class BoundMethodDispatcher(Mapping): + """Wraps a MethodDispatcher, binding its return values to `instance`""" + def __init__(self, instance, dispatcher): + self.instance = instance + self.dispatcher = dispatcher + + def __getitem__(self, key): + # see https://docs.python.org/3/reference/datamodel.html#object.__get__ + # on a function, __get__ is used to bind a function to an instance as a bound method + return self.dispatcher[key].__get__(self.instance) + + def get(self, key, default): + if key in self.dispatcher: + return self[key] + else: + return default + + def __iter__(self): + return iter(self.dispatcher) + + def __len__(self): + return len(self.dispatcher) + + def __contains__(self, key): + return key in self.dispatcher + # Some utility functions to deal with weirdness around UCS2 vs UCS4 # python builds diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/constants.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/constants.py index 1ff8041..fe3e237 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/constants.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/constants.py @@ -519,8 +519,8 @@ adjustForeignAttributes = { "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) } -unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in - adjustForeignAttributes.items()]) +unadjustForeignAttributes = {(ns, local): qname for qname, (prefix, local, ns) in + adjustForeignAttributes.items()} spaceCharacters = frozenset([ "\t", @@ -544,8 +544,7 @@ asciiLetters = frozenset(string.ascii_letters) digits = frozenset(string.digits) hexDigits = frozenset(string.hexdigits) -asciiUpper2Lower = dict([(ord(c), ord(c.lower())) - for c in string.ascii_uppercase]) +asciiUpper2Lower = {ord(c): ord(c.lower()) for c in string.ascii_uppercase} # Heading elements need to be ordered headingElements = ( @@ -2934,7 +2933,7 @@ tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], tokenTypes["EmptyTag"]]) -prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes = {v: k for k, v in namespaces.items()} prefixes["http://www.w3.org/1998/Math/MathML"] = "math" diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/sanitizer.py index af8e77b..aa7431d 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/sanitizer.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -1,6 +1,15 @@ +"""Deprecated from html5lib 1.1. + +See `here `_ for +information about its deprecation; `Bleach `_ +is recommended as a replacement. Please let us know in the aforementioned issue +if Bleach is unsuitable for your needs. + +""" from __future__ import absolute_import, division, unicode_literals import re +import warnings from xml.sax.saxutils import escape, unescape from pip._vendor.six.moves import urllib_parse as urlparse @@ -11,6 +20,14 @@ from ..constants import namespaces, prefixes __all__ = ["Filter"] +_deprecation_msg = ( + "html5lib's sanitizer is deprecated; see " + + "https://github.com/html5lib/html5lib-python/issues/443 and please let " + + "us know if Bleach is unsuitable for your needs" +) + +warnings.warn(_deprecation_msg, DeprecationWarning) + allowed_elements = frozenset(( (namespaces['html'], 'a'), (namespaces['html'], 'abbr'), @@ -750,6 +767,9 @@ class Filter(base.Filter): """ super(Filter, self).__init__(source) + + warnings.warn(_deprecation_msg, DeprecationWarning) + self.allowed_elements = allowed_elements self.allowed_attributes = allowed_attributes self.allowed_css_properties = allowed_css_properties diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/html5parser.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/html5parser.py index ae41a13..d06784f 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/html5parser.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/html5parser.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, division, unicode_literals from pip._vendor.six import with_metaclass, viewkeys import types -from collections import OrderedDict from . import _inputstream from . import _tokenizer @@ -119,8 +118,8 @@ class HTMLParser(object): self.tree = tree(namespaceHTMLElements) self.errors = [] - self.phases = dict([(name, cls(self, self.tree)) for name, cls in - getPhases(debug).items()]) + self.phases = {name: cls(self, self.tree) for name, cls in + getPhases(debug).items()} def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): @@ -202,7 +201,7 @@ class HTMLParser(object): DoctypeToken = tokenTypes["Doctype"] ParseErrorToken = tokenTypes["ParseError"] - for token in self.normalizedTokens(): + for token in self.tokenizer: prev_token = None new_token = token while new_token is not None: @@ -260,10 +259,6 @@ class HTMLParser(object): if reprocess: assert self.phase not in phases - def normalizedTokens(self): - for token in self.tokenizer: - yield self.normalizeToken(token) - def parse(self, stream, *args, **kwargs): """Parse a HTML document into a well-formed tree @@ -325,17 +320,6 @@ class HTMLParser(object): if self.strict: raise ParseError(E[errorcode] % datavars) - def normalizeToken(self, token): - # HTML5 specific normalizations to the token stream - if token["type"] == tokenTypes["StartTag"]: - raw = token["data"] - token["data"] = OrderedDict(raw) - if len(raw) > len(token["data"]): - # we had some duplicated attribute, fix so first wins - token["data"].update(raw[::-1]) - - return token - def adjustMathMLAttributes(self, token): adjust_attributes(token, adjustMathMLAttributes) @@ -413,16 +397,12 @@ class HTMLParser(object): def getPhases(debug): def log(function): """Logger that records which phase processes each token""" - type_names = dict((value, key) for key, value in - tokenTypes.items()) + type_names = {value: key for key, value in tokenTypes.items()} def wrapped(self, *args, **kwargs): if function.__name__.startswith("process") and len(args) > 0: token = args[0] - try: - info = {"type": type_names[token['type']]} - except: - raise + info = {"type": type_names[token['type']]} if token['type'] in tagTokenTypes: info["name"] = token['name'] @@ -446,10 +426,13 @@ def getPhases(debug): class Phase(with_metaclass(getMetaclass(debug, log))): """Base class for helper object that implements each phase of processing """ + __slots__ = ("parser", "tree", "__startTagCache", "__endTagCache") def __init__(self, parser, tree): self.parser = parser self.tree = tree + self.__startTagCache = {} + self.__endTagCache = {} def processEOF(self): raise NotImplementedError @@ -469,7 +452,21 @@ def getPhases(debug): self.tree.insertText(token["data"]) def processStartTag(self, token): - return self.startTagHandler[token["name"]](token) + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__startTagCache: + func = self.__startTagCache[name] + else: + func = self.__startTagCache[name] = self.startTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__startTagCache) > len(self.startTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__startTagCache.pop(next(iter(self.__startTagCache))) + return func(token) def startTagHtml(self, token): if not self.parser.firstStartTag and token["name"] == "html": @@ -482,9 +479,25 @@ def getPhases(debug): self.parser.firstStartTag = False def processEndTag(self, token): - return self.endTagHandler[token["name"]](token) + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__endTagCache: + func = self.__endTagCache[name] + else: + func = self.__endTagCache[name] = self.endTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__endTagCache) > len(self.endTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__endTagCache.pop(next(iter(self.__endTagCache))) + return func(token) class InitialPhase(Phase): + __slots__ = tuple() + def processSpaceCharacters(self, token): pass @@ -613,6 +626,8 @@ def getPhases(debug): return True class BeforeHtmlPhase(Phase): + __slots__ = tuple() + # helper methods def insertHtmlElement(self): self.tree.insertRoot(impliedTagToken("html", "StartTag")) @@ -648,19 +663,7 @@ def getPhases(debug): return token class BeforeHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("head", "body", "html", "br"), self.endTagImplyHead) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): self.startTagHead(impliedTagToken("head", "StartTag")) @@ -693,28 +696,19 @@ def getPhases(debug): self.parser.parseError("end-tag-after-implied-root", {"name": token["name"]}) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), endTagImplyHead) + ]) + endTagHandler.default = endTagOther + class InHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("title", self.startTagTitle), - (("noframes", "style"), self.startTagNoFramesStyle), - ("noscript", self.startTagNoscript), - ("script", self.startTagScript), - (("base", "basefont", "bgsound", "command", "link"), - self.startTagBaseLinkCommand), - ("meta", self.startTagMeta), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("head", self.endTagHead), - (("br", "html", "body"), self.endTagHtmlBodyBr) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # the real thing def processEOF(self): @@ -796,22 +790,27 @@ def getPhases(debug): def anythingElse(self): self.endTagHead(impliedTagToken("head")) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("title", startTagTitle), + (("noframes", "style"), startTagNoFramesStyle), + ("noscript", startTagNoscript), + ("script", startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + startTagBaseLinkCommand), + ("meta", startTagMeta), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("head", endTagHead), + (("br", "html", "body"), endTagHtmlBodyBr) + ]) + endTagHandler.default = endTagOther + class InHeadNoscriptPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("basefont", "bgsound", "link", "meta", "noframes", "style"), self.startTagBaseLinkCommand), - (("head", "noscript"), self.startTagHeadNoscript), - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("noscript", self.endTagNoscript), - ("br", self.endTagBr), - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): self.parser.parseError("eof-in-head-noscript") @@ -860,23 +859,21 @@ def getPhases(debug): # Caller must raise parse error first! self.endTagNoscript(impliedTagToken("noscript")) - class AfterHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), startTagBaseLinkCommand), + (("head", "noscript"), startTagHeadNoscript), + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("body", self.startTagBody), - ("frameset", self.startTagFrameset), - (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", - "style", "title"), - self.startTagFromHead), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - self.endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), - self.endTagHtmlBodyBr)]) - self.endTagHandler.default = self.endTagOther + endTagHandler = _utils.MethodDispatcher([ + ("noscript", endTagNoscript), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + + class AfterHeadPhase(Phase): + __slots__ = tuple() def processEOF(self): self.anythingElse() @@ -927,80 +924,30 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] self.parser.framesetOK = True + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + startTagFromHead), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + endTagHtmlBodyBr)]) + endTagHandler.default = endTagOther + class InBodyPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody # the really-really-really-very crazy mode - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + __slots__ = ("processSpaceCharacters",) + def __init__(self, *args, **kwargs): + super(InBodyPhase, self).__init__(*args, **kwargs) # Set this to the default handler self.processSpaceCharacters = self.processSpaceCharactersNonPre - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("base", "basefont", "bgsound", "command", "link", "meta", - "script", "style", "title"), - self.startTagProcessInHead), - ("body", self.startTagBody), - ("frameset", self.startTagFrameset), - (("address", "article", "aside", "blockquote", "center", "details", - "dir", "div", "dl", "fieldset", "figcaption", "figure", - "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", - "section", "summary", "ul"), - self.startTagCloseP), - (headingElements, self.startTagHeading), - (("pre", "listing"), self.startTagPreListing), - ("form", self.startTagForm), - (("li", "dd", "dt"), self.startTagListItem), - ("plaintext", self.startTagPlaintext), - ("a", self.startTagA), - (("b", "big", "code", "em", "font", "i", "s", "small", "strike", - "strong", "tt", "u"), self.startTagFormatting), - ("nobr", self.startTagNobr), - ("button", self.startTagButton), - (("applet", "marquee", "object"), self.startTagAppletMarqueeObject), - ("xmp", self.startTagXmp), - ("table", self.startTagTable), - (("area", "br", "embed", "img", "keygen", "wbr"), - self.startTagVoidFormatting), - (("param", "source", "track"), self.startTagParamSource), - ("input", self.startTagInput), - ("hr", self.startTagHr), - ("image", self.startTagImage), - ("isindex", self.startTagIsIndex), - ("textarea", self.startTagTextarea), - ("iframe", self.startTagIFrame), - ("noscript", self.startTagNoscript), - (("noembed", "noframes"), self.startTagRawtext), - ("select", self.startTagSelect), - (("rp", "rt"), self.startTagRpRt), - (("option", "optgroup"), self.startTagOpt), - (("math"), self.startTagMath), - (("svg"), self.startTagSvg), - (("caption", "col", "colgroup", "frame", "head", - "tbody", "td", "tfoot", "th", "thead", - "tr"), self.startTagMisplaced) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("body", self.endTagBody), - ("html", self.endTagHtml), - (("address", "article", "aside", "blockquote", "button", "center", - "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", - "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", - "section", "summary", "ul"), self.endTagBlock), - ("form", self.endTagForm), - ("p", self.endTagP), - (("dd", "dt", "li"), self.endTagListItem), - (headingElements, self.endTagHeading), - (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", - "strike", "strong", "tt", "u"), self.endTagFormatting), - (("applet", "marquee", "object"), self.endTagAppletMarqueeObject), - ("br", self.endTagBr), - ]) - self.endTagHandler.default = self.endTagOther - def isMatchingFormattingElement(self, node1, node2): return (node1.name == node2.name and node1.namespace == node2.namespace and @@ -1650,14 +1597,73 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) break + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("base", "basefont", "bgsound", "command", "link", "meta", + "script", "style", "title"), + startTagProcessInHead), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("address", "article", "aside", "blockquote", "center", "details", + "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", + "section", "summary", "ul"), + startTagCloseP), + (headingElements, startTagHeading), + (("pre", "listing"), startTagPreListing), + ("form", startTagForm), + (("li", "dd", "dt"), startTagListItem), + ("plaintext", startTagPlaintext), + ("a", startTagA), + (("b", "big", "code", "em", "font", "i", "s", "small", "strike", + "strong", "tt", "u"), startTagFormatting), + ("nobr", startTagNobr), + ("button", startTagButton), + (("applet", "marquee", "object"), startTagAppletMarqueeObject), + ("xmp", startTagXmp), + ("table", startTagTable), + (("area", "br", "embed", "img", "keygen", "wbr"), + startTagVoidFormatting), + (("param", "source", "track"), startTagParamSource), + ("input", startTagInput), + ("hr", startTagHr), + ("image", startTagImage), + ("isindex", startTagIsIndex), + ("textarea", startTagTextarea), + ("iframe", startTagIFrame), + ("noscript", startTagNoscript), + (("noembed", "noframes"), startTagRawtext), + ("select", startTagSelect), + (("rp", "rt"), startTagRpRt), + (("option", "optgroup"), startTagOpt), + (("math"), startTagMath), + (("svg"), startTagSvg), + (("caption", "col", "colgroup", "frame", "head", + "tbody", "td", "tfoot", "th", "thead", + "tr"), startTagMisplaced) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("body", endTagBody), + ("html", endTagHtml), + (("address", "article", "aside", "blockquote", "button", "center", + "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", + "section", "summary", "ul"), endTagBlock), + ("form", endTagForm), + ("p", endTagP), + (("dd", "dt", "li"), endTagListItem), + (headingElements, endTagHeading), + (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", + "strike", "strong", "tt", "u"), endTagFormatting), + (("applet", "marquee", "object"), endTagAppletMarqueeObject), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + class TextPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([]) - self.startTagHandler.default = self.startTagOther - self.endTagHandler = _utils.MethodDispatcher([ - ("script", self.endTagScript)]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processCharacters(self, token): self.tree.insertText(token["data"]) @@ -1683,30 +1689,15 @@ def getPhases(debug): self.tree.openElements.pop() self.parser.phase = self.parser.originalPhase + startTagHandler = _utils.MethodDispatcher([]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([ + ("script", endTagScript)]) + endTagHandler.default = endTagOther + class InTablePhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-table - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("caption", self.startTagCaption), - ("colgroup", self.startTagColgroup), - ("col", self.startTagCol), - (("tbody", "tfoot", "thead"), self.startTagRowGroup), - (("td", "th", "tr"), self.startTagImplyTbody), - ("table", self.startTagTable), - (("style", "script"), self.startTagStyleScript), - ("input", self.startTagInput), - ("form", self.startTagForm) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("table", self.endTagTable), - (("body", "caption", "col", "colgroup", "html", "tbody", "td", - "tfoot", "th", "thead", "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods def clearStackToTableContext(self): @@ -1828,9 +1819,32 @@ def getPhases(debug): self.parser.phases["inBody"].processEndTag(token) self.tree.insertFromTable = False + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("caption", startTagCaption), + ("colgroup", startTagColgroup), + ("col", startTagCol), + (("tbody", "tfoot", "thead"), startTagRowGroup), + (("td", "th", "tr"), startTagImplyTbody), + ("table", startTagTable), + (("style", "script"), startTagStyleScript), + ("input", startTagInput), + ("form", startTagForm) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("table", endTagTable), + (("body", "caption", "col", "colgroup", "html", "tbody", "td", + "tfoot", "th", "thead", "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InTableTextPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + __slots__ = ("originalPhase", "characterTokens") + + def __init__(self, *args, **kwargs): + super(InTableTextPhase, self).__init__(*args, **kwargs) self.originalPhase = None self.characterTokens = [] @@ -1875,23 +1889,7 @@ def getPhases(debug): class InCaptionPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-caption - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.startTagTableElement) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("caption", self.endTagCaption), - ("table", self.endTagTable), - (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def ignoreEndTagCaption(self): return not self.tree.elementInScope("caption", variant="table") @@ -1944,23 +1942,24 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inBody"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), startTagTableElement) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("caption", endTagCaption), + ("table", endTagTable), + (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", + "thead", "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InColumnGroupPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-column - - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("col", self.startTagCol) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("colgroup", self.endTagColgroup), - ("col", self.endTagCol) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def ignoreEndTagColgroup(self): return self.tree.openElements[-1].name == "html" @@ -2010,26 +2009,21 @@ def getPhases(debug): if not ignoreEndTag: return token + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("col", startTagCol) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("colgroup", endTagColgroup), + ("col", endTagCol) + ]) + endTagHandler.default = endTagOther + class InTableBodyPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-table0 - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("tr", self.startTagTr), - (("td", "th"), self.startTagTableCell), - (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), - self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), - ("table", self.endTagTable), - (("body", "caption", "col", "colgroup", "html", "td", "th", - "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods def clearStackToTableBodyContext(self): @@ -2108,26 +2102,26 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inTable"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("tr", startTagTr), + (("td", "th"), startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), + startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("tbody", "tfoot", "thead"), endTagTableRowGroup), + ("table", endTagTable), + (("body", "caption", "col", "colgroup", "html", "td", "th", + "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InRowPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-row - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("td", "th"), self.startTagTableCell), - (("caption", "col", "colgroup", "tbody", "tfoot", "thead", - "tr"), self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("tr", self.endTagTr), - ("table", self.endTagTable), - (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), - (("body", "caption", "col", "colgroup", "html", "td", "th"), - self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods (XXX unify this with other table helper methods) def clearStackToTableRowContext(self): @@ -2197,23 +2191,26 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inTable"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("td", "th"), startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead", + "tr"), startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("tr", endTagTr), + ("table", endTagTable), + (("tbody", "tfoot", "thead"), endTagTableRowGroup), + (("body", "caption", "col", "colgroup", "html", "td", "th"), + endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InCellPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-cell - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("td", "th"), self.endTagTableCell), - (("body", "caption", "col", "colgroup", "html"), self.endTagIgnore), - (("table", "tbody", "tfoot", "thead", "tr"), self.endTagImply) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper def closeCell(self): @@ -2273,26 +2270,22 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inBody"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("td", "th"), endTagTableCell), + (("body", "caption", "col", "colgroup", "html"), endTagIgnore), + (("table", "tbody", "tfoot", "thead", "tr"), endTagImply) + ]) + endTagHandler.default = endTagOther + class InSelectPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("option", self.startTagOption), - ("optgroup", self.startTagOptgroup), - ("select", self.startTagSelect), - (("input", "keygen", "textarea"), self.startTagInput), - ("script", self.startTagScript) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("option", self.endTagOption), - ("optgroup", self.endTagOptgroup), - ("select", self.endTagSelect) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # http://www.whatwg.org/specs/web-apps/current-work/#in-select def processEOF(self): @@ -2373,21 +2366,25 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-in-select", {"name": token["name"]}) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("option", startTagOption), + ("optgroup", startTagOptgroup), + ("select", startTagSelect), + (("input", "keygen", "textarea"), startTagInput), + ("script", startTagScript) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("option", endTagOption), + ("optgroup", endTagOptgroup), + ("select", endTagSelect) + ]) + endTagHandler.default = endTagOther + class InSelectInTablePhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), - self.startTagTable) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), - self.endTagTable) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): self.parser.phases["inSelect"].processEOF() @@ -2412,7 +2409,21 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inSelect"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + startTagTable) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + endTagTable) + ]) + endTagHandler.default = endTagOther + class InForeignContentPhase(Phase): + __slots__ = tuple() + breakoutElements = frozenset(["b", "big", "blockquote", "body", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", @@ -2422,9 +2433,6 @@ def getPhases(debug): "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"]) - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - def adjustSVGTagNames(self, token): replacements = {"altglyph": "altGlyph", "altglyphdef": "altGlyphDef", @@ -2478,7 +2486,7 @@ def getPhases(debug): currentNode = self.tree.openElements[-1] if (token["name"] in self.breakoutElements or (token["name"] == "font" and - set(token["data"].keys()) & set(["color", "face", "size"]))): + set(token["data"].keys()) & {"color", "face", "size"})): self.parser.parseError("unexpected-html-element-in-foreign-content", {"name": token["name"]}) while (self.tree.openElements[-1].namespace != @@ -2528,16 +2536,7 @@ def getPhases(debug): return new_token class AfterBodyPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([("html", self.endTagHtml)]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): # Stop parsing @@ -2574,23 +2573,17 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] return token + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([("html", endTagHtml)]) + endTagHandler.default = endTagOther + class InFramesetPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("frameset", self.startTagFrameset), - ("frame", self.startTagFrame), - ("noframes", self.startTagNoframes) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("frameset", self.endTagFrameset) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): if self.tree.openElements[-1].name != "html": @@ -2631,21 +2624,22 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-in-frameset", {"name": token["name"]}) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("frameset", startTagFrameset), + ("frame", startTagFrame), + ("noframes", startTagNoframes) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("frameset", endTagFrameset) + ]) + endTagHandler.default = endTagOther + class AfterFramesetPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#after3 - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("noframes", self.startTagNoframes) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("html", self.endTagHtml) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): # Stop parsing @@ -2668,14 +2662,19 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-after-frameset", {"name": token["name"]}) - class AfterAfterBodyPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("noframes", startTagNoframes) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml) - ]) - self.startTagHandler.default = self.startTagOther + endTagHandler = _utils.MethodDispatcher([ + ("html", endTagHtml) + ]) + endTagHandler.default = endTagOther + + class AfterAfterBodyPhase(Phase): + __slots__ = tuple() def processEOF(self): pass @@ -2706,15 +2705,13 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] return token - class AfterAfterFramesetPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("noframes", self.startTagNoFrames) - ]) - self.startTagHandler.default = self.startTagOther + class AfterAfterFramesetPhase(Phase): + __slots__ = tuple() def processEOF(self): pass @@ -2741,6 +2738,13 @@ def getPhases(debug): def processEndTag(self, token): self.parser.parseError("expected-eof-but-got-end-tag", {"name": token["name"]}) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("noframes", startTagNoFrames) + ]) + startTagHandler.default = startTagOther + # pylint:enable=unused-argument return { @@ -2774,8 +2778,8 @@ def getPhases(debug): def adjust_attributes(token, replacements): needs_adjustment = viewkeys(token['data']) & viewkeys(replacements) if needs_adjustment: - token['data'] = OrderedDict((replacements.get(k, k), v) - for k, v in token['data'].items()) + token['data'] = type(token['data'])((replacements.get(k, k), v) + for k, v in token['data'].items()) def impliedTagToken(name, type="EndTag", attributes=None, diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/serializer.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/serializer.py index 53f4d44..d5669d8 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/serializer.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/serializer.py @@ -274,7 +274,7 @@ class HTMLSerializer(object): if token["systemId"]: if token["systemId"].find('"') >= 0: if token["systemId"].find("'") >= 0: - self.serializeError("System identifer contains both single and double quote characters") + self.serializeError("System identifier contains both single and double quote characters") quote_char = "'" else: quote_char = '"' diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/base.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/base.py index 73973db..965fce2 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/base.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/base.py @@ -10,9 +10,9 @@ Marker = None listElementsMap = { None: (frozenset(scopingElements), False), - "button": (frozenset(scopingElements | set([(namespaces["html"], "button")])), False), - "list": (frozenset(scopingElements | set([(namespaces["html"], "ol"), - (namespaces["html"], "ul")])), False), + "button": (frozenset(scopingElements | {(namespaces["html"], "button")}), False), + "list": (frozenset(scopingElements | {(namespaces["html"], "ol"), + (namespaces["html"], "ul")}), False), "table": (frozenset([(namespaces["html"], "html"), (namespaces["html"], "table")]), False), "select": (frozenset([(namespaces["html"], "optgroup"), @@ -28,7 +28,7 @@ class Node(object): :arg name: The tag name associated with the node """ - # The tag name assocaited with the node + # The tag name associated with the node self.name = name # The parent of the current node (or None for the document node) self.parent = None diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree.py index 0dedf44..ea92dc3 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree.py @@ -5,6 +5,8 @@ from pip._vendor.six import text_type import re +from copy import copy + from . import base from .. import _ihatexml from .. import constants @@ -61,16 +63,17 @@ def getETreeBuilder(ElementTreeImplementation, fullTree=False): return self._element.attrib def _setAttributes(self, attributes): - # Delete existing attributes first - # XXX - there may be a better way to do this... - for key in list(self._element.attrib.keys()): - del self._element.attrib[key] - for key, value in attributes.items(): - if isinstance(key, tuple): - name = "{%s}%s" % (key[2], key[1]) - else: - name = key - self._element.set(name, value) + el_attrib = self._element.attrib + el_attrib.clear() + if attributes: + # calling .items _always_ allocates, and the above truthy check is cheaper than the + # allocation on average + for key, value in attributes.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], key[1]) + else: + name = key + el_attrib[name] = value attributes = property(_getAttributes, _setAttributes) @@ -129,8 +132,8 @@ def getETreeBuilder(ElementTreeImplementation, fullTree=False): def cloneNode(self): element = type(self)(self.name, self.namespace) - for name, value in self.attributes.items(): - element.attributes[name] = value + if self._element.attrib: + element._element.attrib = copy(self._element.attrib) return element def reparentChildren(self, newParent): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py index ca12a99..f037759 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py @@ -16,6 +16,11 @@ import warnings import re import sys +try: + from collections.abc import MutableMapping +except ImportError: + from collections import MutableMapping + from . import base from ..constants import DataLossWarning from .. import constants @@ -23,6 +28,7 @@ from . import etree as etree_builders from .. import _ihatexml import lxml.etree as etree +from pip._vendor.six import PY3, binary_type fullTree = True @@ -44,7 +50,11 @@ class Document(object): self._childNodes = [] def appendChild(self, element): - self._elementTree.getroot().addnext(element._element) + last = self._elementTree.getroot() + for last in self._elementTree.getroot().itersiblings(): + pass + + last.addnext(element._element) def _getChildNodes(self): return self._childNodes @@ -185,26 +195,37 @@ class TreeBuilder(base.TreeBuilder): infosetFilter = self.infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) self.namespaceHTMLElements = namespaceHTMLElements - class Attributes(dict): - def __init__(self, element, value=None): - if value is None: - value = {} + class Attributes(MutableMapping): + def __init__(self, element): self._element = element - dict.__init__(self, value) # pylint:disable=non-parent-init-called - for key, value in self.items(): - if isinstance(key, tuple): - name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) - else: - name = infosetFilter.coerceAttribute(key) - self._element._element.attrib[name] = value - def __setitem__(self, key, value): - dict.__setitem__(self, key, value) + def _coerceKey(self, key): if isinstance(key, tuple): name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) else: name = infosetFilter.coerceAttribute(key) - self._element._element.attrib[name] = value + return name + + def __getitem__(self, key): + value = self._element._element.attrib[self._coerceKey(key)] + if not PY3 and isinstance(value, binary_type): + value = value.decode("ascii") + return value + + def __setitem__(self, key, value): + self._element._element.attrib[self._coerceKey(key)] = value + + def __delitem__(self, key): + del self._element._element.attrib[self._coerceKey(key)] + + def __iter__(self): + return iter(self._element._element.attrib) + + def __len__(self): + return len(self._element._element.attrib) + + def clear(self): + return self._element._element.attrib.clear() class Element(builder.Element): def __init__(self, name, namespace): @@ -225,8 +246,10 @@ class TreeBuilder(base.TreeBuilder): def _getAttributes(self): return self._attributes - def _setAttributes(self, attributes): - self._attributes = Attributes(self, attributes) + def _setAttributes(self, value): + attributes = self.attributes + attributes.clear() + attributes.update(value) attributes = property(_getAttributes, _setAttributes) @@ -234,8 +257,11 @@ class TreeBuilder(base.TreeBuilder): data = infosetFilter.coerceCharacters(data) builder.Element.insertText(self, data, insertBefore) - def appendChild(self, child): - builder.Element.appendChild(self, child) + def cloneNode(self): + element = type(self)(self.name, self.namespace) + if self._element.attrib: + element._element.attrib.update(self._element.attrib) + return element class Comment(builder.Comment): def __init__(self, data): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py index 9bec207..b2d3aac 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py @@ -2,10 +2,10 @@ tree, generating tokens identical to those produced by the tokenizer module. -To create a tree walker for a new type of tree, you need to do +To create a tree walker for a new type of tree, you need to implement a tree walker object (called TreeWalker by convention) that -implements a 'serialize' method taking a tree as sole argument and -returning an iterator generating tokens. +implements a 'serialize' method which takes a tree as sole argument and +returns an iterator which generates tokens. """ from __future__ import absolute_import, division, unicode_literals diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree.py index 95fc0c1..837b27e 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree.py @@ -127,4 +127,5 @@ def getETreeBuilder(ElementTreeImplementation): return locals() + getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py index e81ddf3..c56af39 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py @@ -1,6 +1,8 @@ from __future__ import absolute_import, division, unicode_literals from pip._vendor.six import text_type +from collections import OrderedDict + from lxml import etree from ..treebuilders.etree import tag_regexp @@ -163,7 +165,7 @@ class TreeWalker(base.NonRecursiveTreeWalker): else: namespace = None tag = ensure_str(node.tag) - attrs = {} + attrs = OrderedDict() for name, value in list(node.attrib.items()): name = ensure_str(name) value = ensure_str(value) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/idna/core.py b/venv/lib/python3.8/site-packages/pip/_vendor/idna/core.py index 9c3bba2..41ec5c7 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/idna/core.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/idna/core.py @@ -300,6 +300,8 @@ def ulabel(label): label = label.lower() if label.startswith(_alabel_prefix): label = label[len(_alabel_prefix):] + if not label: + raise IDNAError('Malformed A-label, no Punycode eligible content found') if label.decode('ascii')[-1] == '-': raise IDNAError('A-label must not end with a hyphen') else: diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/idna/idnadata.py b/venv/lib/python3.8/site-packages/pip/_vendor/idna/idnadata.py index 2b81c52..a284e4c 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/idna/idnadata.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/idna/idnadata.py @@ -1,6 +1,6 @@ # This file is automatically generated by tools/idna-data -__version__ = "12.1.0" +__version__ = "13.0.0" scripts = { 'Greek': ( 0x37000000374, @@ -48,16 +48,18 @@ scripts = { 0x300700003008, 0x30210000302a, 0x30380000303c, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xf9000000fa6e, 0xfa700000fada, - 0x200000002a6d7, + 0x16ff000016ff2, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, 0x2f8000002fa1e, + 0x300000003134b, ), 'Hebrew': ( 0x591000005c8, @@ -389,9 +391,9 @@ joining_types = { 0x853: 68, 0x854: 82, 0x855: 68, - 0x856: 85, - 0x857: 85, - 0x858: 85, + 0x856: 82, + 0x857: 82, + 0x858: 82, 0x860: 68, 0x861: 85, 0x862: 68, @@ -432,6 +434,16 @@ joining_types = { 0x8bb: 68, 0x8bc: 68, 0x8bd: 68, + 0x8be: 68, + 0x8bf: 68, + 0x8c0: 68, + 0x8c1: 68, + 0x8c2: 68, + 0x8c3: 68, + 0x8c4: 68, + 0x8c5: 68, + 0x8c6: 68, + 0x8c7: 68, 0x8e2: 85, 0x1806: 85, 0x1807: 68, @@ -756,6 +768,34 @@ joining_types = { 0x10f52: 68, 0x10f53: 68, 0x10f54: 82, + 0x10fb0: 68, + 0x10fb1: 85, + 0x10fb2: 68, + 0x10fb3: 68, + 0x10fb4: 82, + 0x10fb5: 82, + 0x10fb6: 82, + 0x10fb7: 85, + 0x10fb8: 68, + 0x10fb9: 82, + 0x10fba: 82, + 0x10fbb: 68, + 0x10fbc: 68, + 0x10fbd: 82, + 0x10fbe: 68, + 0x10fbf: 68, + 0x10fc0: 85, + 0x10fc1: 68, + 0x10fc2: 82, + 0x10fc3: 82, + 0x10fc4: 68, + 0x10fc5: 85, + 0x10fc6: 85, + 0x10fc7: 85, + 0x10fc8: 85, + 0x10fc9: 82, + 0x10fca: 68, + 0x10fcb: 76, 0x110bd: 85, 0x110cd: 85, 0x1e900: 68, @@ -1129,7 +1169,7 @@ codepoint_classes = { 0x8400000085c, 0x8600000086b, 0x8a0000008b5, - 0x8b6000008be, + 0x8b6000008c8, 0x8d3000008e2, 0x8e300000958, 0x96000000964, @@ -1188,7 +1228,7 @@ codepoint_classes = { 0xb3c00000b45, 0xb4700000b49, 0xb4b00000b4e, - 0xb5600000b58, + 0xb5500000b58, 0xb5f00000b64, 0xb6600000b70, 0xb7100000b72, @@ -1233,8 +1273,7 @@ codepoint_classes = { 0xce000000ce4, 0xce600000cf0, 0xcf100000cf3, - 0xd0000000d04, - 0xd0500000d0d, + 0xd0000000d0d, 0xd0e00000d11, 0xd1200000d45, 0xd4600000d49, @@ -1243,7 +1282,7 @@ codepoint_classes = { 0xd5f00000d64, 0xd6600000d70, 0xd7a00000d80, - 0xd8200000d84, + 0xd8100000d84, 0xd8500000d97, 0xd9a00000db2, 0xdb300000dbc, @@ -1358,6 +1397,7 @@ codepoint_classes = { 0x1a9000001a9a, 0x1aa700001aa8, 0x1ab000001abe, + 0x1abf00001ac1, 0x1b0000001b4c, 0x1b5000001b5a, 0x1b6b00001b74, @@ -1609,10 +1649,10 @@ codepoint_classes = { 0x30a1000030fb, 0x30fc000030ff, 0x310500003130, - 0x31a0000031bb, + 0x31a0000031c0, 0x31f000003200, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xa0000000a48d, 0xa4d00000a4fe, 0xa5000000a60d, @@ -1727,8 +1767,11 @@ codepoint_classes = { 0xa7bd0000a7be, 0xa7bf0000a7c0, 0xa7c30000a7c4, - 0xa7f70000a7f8, + 0xa7c80000a7c9, + 0xa7ca0000a7cb, + 0xa7f60000a7f8, 0xa7fa0000a828, + 0xa82c0000a82d, 0xa8400000a874, 0xa8800000a8c6, 0xa8d00000a8da, @@ -1753,7 +1796,7 @@ codepoint_classes = { 0xab200000ab27, 0xab280000ab2f, 0xab300000ab5b, - 0xab600000ab68, + 0xab600000ab6a, 0xabc00000abeb, 0xabec0000abee, 0xabf00000abfa, @@ -1827,9 +1870,13 @@ codepoint_classes = { 0x10cc000010cf3, 0x10d0000010d28, 0x10d3000010d3a, + 0x10e8000010eaa, + 0x10eab00010ead, + 0x10eb000010eb2, 0x10f0000010f1d, 0x10f2700010f28, 0x10f3000010f51, + 0x10fb000010fc5, 0x10fe000010ff7, 0x1100000011047, 0x1106600011070, @@ -1838,12 +1885,12 @@ codepoint_classes = { 0x110f0000110fa, 0x1110000011135, 0x1113600011140, - 0x1114400011147, + 0x1114400011148, 0x1115000011174, 0x1117600011177, 0x11180000111c5, 0x111c9000111cd, - 0x111d0000111db, + 0x111ce000111db, 0x111dc000111dd, 0x1120000011212, 0x1121300011238, @@ -1872,7 +1919,7 @@ codepoint_classes = { 0x1137000011375, 0x114000001144b, 0x114500001145a, - 0x1145e00011460, + 0x1145e00011462, 0x11480000114c6, 0x114c7000114c8, 0x114d0000114da, @@ -1889,7 +1936,14 @@ codepoint_classes = { 0x117300001173a, 0x118000001183b, 0x118c0000118ea, - 0x118ff00011900, + 0x118ff00011907, + 0x119090001190a, + 0x1190c00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193b00011944, + 0x119500001195a, 0x119a0000119a8, 0x119aa000119d8, 0x119da000119e2, @@ -1920,6 +1974,7 @@ codepoint_classes = { 0x11d9300011d99, 0x11da000011daa, 0x11ee000011ef7, + 0x11fb000011fb1, 0x120000001239a, 0x1248000012544, 0x130000001342f, @@ -1939,9 +1994,11 @@ codepoint_classes = { 0x16f4f00016f88, 0x16f8f00016fa0, 0x16fe000016fe2, - 0x16fe300016fe4, + 0x16fe300016fe5, + 0x16ff000016ff2, 0x17000000187f8, - 0x1880000018af3, + 0x1880000018cd6, + 0x18d0000018d09, 0x1b0000001b11f, 0x1b1500001b153, 0x1b1640001b168, @@ -1971,11 +2028,13 @@ codepoint_classes = { 0x1e8d00001e8d7, 0x1e9220001e94c, 0x1e9500001e95a, - 0x200000002a6d7, + 0x1fbf00001fbfa, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, + 0x300000003134b, ), 'CONTEXTJ': ( 0x200c0000200e, diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/idna/package_data.py b/venv/lib/python3.8/site-packages/pip/_vendor/idna/package_data.py index b5d8216..ce1c521 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/idna/package_data.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/idna/package_data.py @@ -1,2 +1,2 @@ -__version__ = '2.9' +__version__ = '2.10' diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/idna/uts46data.py b/venv/lib/python3.8/site-packages/pip/_vendor/idna/uts46data.py index 2711136..3766dd4 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/idna/uts46data.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/idna/uts46data.py @@ -4,7 +4,7 @@ """IDNA Mapping Table from UTS46.""" -__version__ = "12.1.0" +__version__ = "13.0.0" def _seg_0(): return [ (0x0, '3'), @@ -1074,7 +1074,7 @@ def _seg_10(): (0x8A0, 'V'), (0x8B5, 'X'), (0x8B6, 'V'), - (0x8BE, 'X'), + (0x8C8, 'X'), (0x8D3, 'V'), (0x8E2, 'X'), (0x8E3, 'V'), @@ -1205,7 +1205,7 @@ def _seg_11(): (0xB49, 'X'), (0xB4B, 'V'), (0xB4E, 'X'), - (0xB56, 'V'), + (0xB55, 'V'), (0xB58, 'X'), (0xB5C, 'M', u'ଡ଼'), (0xB5D, 'M', u'ଢ଼'), @@ -1299,8 +1299,6 @@ def _seg_12(): (0xCF1, 'V'), (0xCF3, 'X'), (0xD00, 'V'), - (0xD04, 'X'), - (0xD05, 'V'), (0xD0D, 'X'), (0xD0E, 'V'), (0xD11, 'X'), @@ -1314,7 +1312,7 @@ def _seg_12(): (0xD64, 'X'), (0xD66, 'V'), (0xD80, 'X'), - (0xD82, 'V'), + (0xD81, 'V'), (0xD84, 'X'), (0xD85, 'V'), (0xD97, 'X'), @@ -1355,12 +1353,12 @@ def _seg_12(): (0xEA5, 'V'), (0xEA6, 'X'), (0xEA7, 'V'), + (0xEB3, 'M', u'ໍາ'), + (0xEB4, 'V'), ] def _seg_13(): return [ - (0xEB3, 'M', u'ໍາ'), - (0xEB4, 'V'), (0xEBE, 'X'), (0xEC0, 'V'), (0xEC5, 'X'), @@ -1459,12 +1457,12 @@ def _seg_13(): (0x12C8, 'V'), (0x12D7, 'X'), (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), ] def _seg_14(): return [ - (0x1311, 'X'), - (0x1312, 'V'), (0x1316, 'X'), (0x1318, 'V'), (0x135B, 'X'), @@ -1553,7 +1551,7 @@ def _seg_14(): (0x1AA0, 'V'), (0x1AAE, 'X'), (0x1AB0, 'V'), - (0x1ABF, 'X'), + (0x1AC1, 'X'), (0x1B00, 'V'), (0x1B4C, 'X'), (0x1B50, 'V'), @@ -1563,12 +1561,12 @@ def _seg_14(): (0x1BFC, 'V'), (0x1C38, 'X'), (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), ] def _seg_15(): return [ - (0x1C4A, 'X'), - (0x1C4D, 'V'), (0x1C80, 'M', u'в'), (0x1C81, 'M', u'д'), (0x1C82, 'M', u'о'), @@ -1667,12 +1665,12 @@ def _seg_15(): (0x1D4E, 'V'), (0x1D4F, 'M', u'k'), (0x1D50, 'M', u'm'), + (0x1D51, 'M', u'ŋ'), + (0x1D52, 'M', u'o'), ] def _seg_16(): return [ - (0x1D51, 'M', u'ŋ'), - (0x1D52, 'M', u'o'), (0x1D53, 'M', u'ɔ'), (0x1D54, 'M', u'ᴖ'), (0x1D55, 'M', u'ᴗ'), @@ -1771,12 +1769,12 @@ def _seg_16(): (0x1E1C, 'M', u'ḝ'), (0x1E1D, 'V'), (0x1E1E, 'M', u'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', u'ḡ'), ] def _seg_17(): return [ - (0x1E1F, 'V'), - (0x1E20, 'M', u'ḡ'), (0x1E21, 'V'), (0x1E22, 'M', u'ḣ'), (0x1E23, 'V'), @@ -1875,12 +1873,12 @@ def _seg_17(): (0x1E80, 'M', u'ẁ'), (0x1E81, 'V'), (0x1E82, 'M', u'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', u'ẅ'), ] def _seg_18(): return [ - (0x1E83, 'V'), - (0x1E84, 'M', u'ẅ'), (0x1E85, 'V'), (0x1E86, 'M', u'ẇ'), (0x1E87, 'V'), @@ -1979,12 +1977,12 @@ def _seg_18(): (0x1EE9, 'V'), (0x1EEA, 'M', u'ừ'), (0x1EEB, 'V'), + (0x1EEC, 'M', u'ử'), + (0x1EED, 'V'), ] def _seg_19(): return [ - (0x1EEC, 'M', u'ử'), - (0x1EED, 'V'), (0x1EEE, 'M', u'ữ'), (0x1EEF, 'V'), (0x1EF0, 'M', u'ự'), @@ -2083,12 +2081,12 @@ def _seg_19(): (0x1F80, 'M', u'ἀι'), (0x1F81, 'M', u'ἁι'), (0x1F82, 'M', u'ἂι'), + (0x1F83, 'M', u'ἃι'), + (0x1F84, 'M', u'ἄι'), ] def _seg_20(): return [ - (0x1F83, 'M', u'ἃι'), - (0x1F84, 'M', u'ἄι'), (0x1F85, 'M', u'ἅι'), (0x1F86, 'M', u'ἆι'), (0x1F87, 'M', u'ἇι'), @@ -2187,12 +2185,12 @@ def _seg_20(): (0x1FEE, '3', u' ̈́'), (0x1FEF, '3', u'`'), (0x1FF0, 'X'), + (0x1FF2, 'M', u'ὼι'), + (0x1FF3, 'M', u'ωι'), ] def _seg_21(): return [ - (0x1FF2, 'M', u'ὼι'), - (0x1FF3, 'M', u'ωι'), (0x1FF4, 'M', u'ώι'), (0x1FF5, 'X'), (0x1FF6, 'V'), @@ -2291,12 +2289,12 @@ def _seg_21(): (0x20C0, 'X'), (0x20D0, 'V'), (0x20F1, 'X'), + (0x2100, '3', u'a/c'), + (0x2101, '3', u'a/s'), ] def _seg_22(): return [ - (0x2100, '3', u'a/c'), - (0x2101, '3', u'a/s'), (0x2102, 'M', u'c'), (0x2103, 'M', u'°c'), (0x2104, 'V'), @@ -2395,12 +2393,12 @@ def _seg_22(): (0x2175, 'M', u'vi'), (0x2176, 'M', u'vii'), (0x2177, 'M', u'viii'), + (0x2178, 'M', u'ix'), + (0x2179, 'M', u'x'), ] def _seg_23(): return [ - (0x2178, 'M', u'ix'), - (0x2179, 'M', u'x'), (0x217A, 'M', u'xi'), (0x217B, 'M', u'xii'), (0x217C, 'M', u'l'), @@ -2499,12 +2497,12 @@ def _seg_23(): (0x24B5, '3', u'(z)'), (0x24B6, 'M', u'a'), (0x24B7, 'M', u'b'), + (0x24B8, 'M', u'c'), + (0x24B9, 'M', u'd'), ] def _seg_24(): return [ - (0x24B8, 'M', u'c'), - (0x24B9, 'M', u'd'), (0x24BA, 'M', u'e'), (0x24BB, 'M', u'f'), (0x24BC, 'M', u'g'), @@ -2566,7 +2564,7 @@ def _seg_24(): (0x2B74, 'X'), (0x2B76, 'V'), (0x2B96, 'X'), - (0x2B98, 'V'), + (0x2B97, 'V'), (0x2C00, 'M', u'ⰰ'), (0x2C01, 'M', u'ⰱ'), (0x2C02, 'M', u'ⰲ'), @@ -2603,12 +2601,12 @@ def _seg_24(): (0x2C21, 'M', u'ⱑ'), (0x2C22, 'M', u'ⱒ'), (0x2C23, 'M', u'ⱓ'), + (0x2C24, 'M', u'ⱔ'), + (0x2C25, 'M', u'ⱕ'), ] def _seg_25(): return [ - (0x2C24, 'M', u'ⱔ'), - (0x2C25, 'M', u'ⱕ'), (0x2C26, 'M', u'ⱖ'), (0x2C27, 'M', u'ⱗ'), (0x2C28, 'M', u'ⱘ'), @@ -2707,12 +2705,12 @@ def _seg_25(): (0x2CBA, 'M', u'ⲻ'), (0x2CBB, 'V'), (0x2CBC, 'M', u'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', u'ⲿ'), ] def _seg_26(): return [ - (0x2CBD, 'V'), - (0x2CBE, 'M', u'ⲿ'), (0x2CBF, 'V'), (0x2CC0, 'M', u'ⳁ'), (0x2CC1, 'V'), @@ -2787,7 +2785,7 @@ def _seg_26(): (0x2DD8, 'V'), (0x2DDF, 'X'), (0x2DE0, 'V'), - (0x2E50, 'X'), + (0x2E53, 'X'), (0x2E80, 'V'), (0x2E9A, 'X'), (0x2E9B, 'V'), @@ -2811,12 +2809,12 @@ def _seg_26(): (0x2F0D, 'M', u'冖'), (0x2F0E, 'M', u'冫'), (0x2F0F, 'M', u'几'), + (0x2F10, 'M', u'凵'), + (0x2F11, 'M', u'刀'), ] def _seg_27(): return [ - (0x2F10, 'M', u'凵'), - (0x2F11, 'M', u'刀'), (0x2F12, 'M', u'力'), (0x2F13, 'M', u'勹'), (0x2F14, 'M', u'匕'), @@ -2915,12 +2913,12 @@ def _seg_27(): (0x2F71, 'M', u'禸'), (0x2F72, 'M', u'禾'), (0x2F73, 'M', u'穴'), + (0x2F74, 'M', u'立'), + (0x2F75, 'M', u'竹'), ] def _seg_28(): return [ - (0x2F74, 'M', u'立'), - (0x2F75, 'M', u'竹'), (0x2F76, 'M', u'米'), (0x2F77, 'M', u'糸'), (0x2F78, 'M', u'缶'), @@ -3019,12 +3017,12 @@ def _seg_28(): (0x2FD5, 'M', u'龠'), (0x2FD6, 'X'), (0x3000, '3', u' '), + (0x3001, 'V'), + (0x3002, 'M', u'.'), ] def _seg_29(): return [ - (0x3001, 'V'), - (0x3002, 'M', u'.'), (0x3003, 'V'), (0x3036, 'M', u'〒'), (0x3037, 'V'), @@ -3123,12 +3121,12 @@ def _seg_29(): (0x317C, 'M', u'ᄯ'), (0x317D, 'M', u'ᄲ'), (0x317E, 'M', u'ᄶ'), + (0x317F, 'M', u'ᅀ'), + (0x3180, 'M', u'ᅇ'), ] def _seg_30(): return [ - (0x317F, 'M', u'ᅀ'), - (0x3180, 'M', u'ᅇ'), (0x3181, 'M', u'ᅌ'), (0x3182, 'M', u'ᇱ'), (0x3183, 'M', u'ᇲ'), @@ -3160,8 +3158,6 @@ def _seg_30(): (0x319E, 'M', u'地'), (0x319F, 'M', u'人'), (0x31A0, 'V'), - (0x31BB, 'X'), - (0x31C0, 'V'), (0x31E4, 'X'), (0x31F0, 'V'), (0x3200, '3', u'(ᄀ)'), @@ -3227,14 +3223,14 @@ def _seg_30(): (0x323C, '3', u'(監)'), (0x323D, '3', u'(企)'), (0x323E, '3', u'(資)'), - ] - -def _seg_31(): - return [ (0x323F, '3', u'(協)'), (0x3240, '3', u'(祭)'), (0x3241, '3', u'(休)'), (0x3242, '3', u'(自)'), + ] + +def _seg_31(): + return [ (0x3243, '3', u'(至)'), (0x3244, 'M', u'問'), (0x3245, 'M', u'幼'), @@ -3331,14 +3327,14 @@ def _seg_31(): (0x32A7, 'M', u'左'), (0x32A8, 'M', u'右'), (0x32A9, 'M', u'医'), - ] - -def _seg_32(): - return [ (0x32AA, 'M', u'宗'), (0x32AB, 'M', u'学'), (0x32AC, 'M', u'監'), (0x32AD, 'M', u'企'), + ] + +def _seg_32(): + return [ (0x32AE, 'M', u'資'), (0x32AF, 'M', u'協'), (0x32B0, 'M', u'夜'), @@ -3435,14 +3431,14 @@ def _seg_32(): (0x330B, 'M', u'カイリ'), (0x330C, 'M', u'カラット'), (0x330D, 'M', u'カロリー'), - ] - -def _seg_33(): - return [ (0x330E, 'M', u'ガロン'), (0x330F, 'M', u'ガンマ'), (0x3310, 'M', u'ギガ'), (0x3311, 'M', u'ギニー'), + ] + +def _seg_33(): + return [ (0x3312, 'M', u'キュリー'), (0x3313, 'M', u'ギルダー'), (0x3314, 'M', u'キロ'), @@ -3539,14 +3535,14 @@ def _seg_33(): (0x336F, 'M', u'23点'), (0x3370, 'M', u'24点'), (0x3371, 'M', u'hpa'), - ] - -def _seg_34(): - return [ (0x3372, 'M', u'da'), (0x3373, 'M', u'au'), (0x3374, 'M', u'bar'), (0x3375, 'M', u'ov'), + ] + +def _seg_34(): + return [ (0x3376, 'M', u'pc'), (0x3377, 'M', u'dm'), (0x3378, 'M', u'dm2'), @@ -3643,14 +3639,14 @@ def _seg_34(): (0x33D3, 'M', u'lx'), (0x33D4, 'M', u'mb'), (0x33D5, 'M', u'mil'), - ] - -def _seg_35(): - return [ (0x33D6, 'M', u'mol'), (0x33D7, 'M', u'ph'), (0x33D8, 'X'), (0x33D9, 'M', u'ppm'), + ] + +def _seg_35(): + return [ (0x33DA, 'M', u'pr'), (0x33DB, 'M', u'sr'), (0x33DC, 'M', u'sv'), @@ -3690,9 +3686,7 @@ def _seg_35(): (0x33FE, 'M', u'31日'), (0x33FF, 'M', u'gal'), (0x3400, 'V'), - (0x4DB6, 'X'), - (0x4DC0, 'V'), - (0x9FF0, 'X'), + (0x9FFD, 'X'), (0xA000, 'V'), (0xA48D, 'X'), (0xA490, 'V'), @@ -3747,16 +3741,16 @@ def _seg_35(): (0xA66D, 'V'), (0xA680, 'M', u'ꚁ'), (0xA681, 'V'), - ] - -def _seg_36(): - return [ (0xA682, 'M', u'ꚃ'), (0xA683, 'V'), (0xA684, 'M', u'ꚅ'), (0xA685, 'V'), (0xA686, 'M', u'ꚇ'), (0xA687, 'V'), + ] + +def _seg_36(): + return [ (0xA688, 'M', u'ꚉ'), (0xA689, 'V'), (0xA68A, 'M', u'ꚋ'), @@ -3851,16 +3845,16 @@ def _seg_36(): (0xA766, 'M', u'ꝧ'), (0xA767, 'V'), (0xA768, 'M', u'ꝩ'), - ] - -def _seg_37(): - return [ (0xA769, 'V'), (0xA76A, 'M', u'ꝫ'), (0xA76B, 'V'), (0xA76C, 'M', u'ꝭ'), (0xA76D, 'V'), (0xA76E, 'M', u'ꝯ'), + ] + +def _seg_37(): + return [ (0xA76F, 'V'), (0xA770, 'M', u'ꝯ'), (0xA771, 'V'), @@ -3935,12 +3929,17 @@ def _seg_37(): (0xA7C4, 'M', u'ꞔ'), (0xA7C5, 'M', u'ʂ'), (0xA7C6, 'M', u'ᶎ'), - (0xA7C7, 'X'), - (0xA7F7, 'V'), + (0xA7C7, 'M', u'ꟈ'), + (0xA7C8, 'V'), + (0xA7C9, 'M', u'ꟊ'), + (0xA7CA, 'V'), + (0xA7CB, 'X'), + (0xA7F5, 'M', u'ꟶ'), + (0xA7F6, 'V'), (0xA7F8, 'M', u'ħ'), (0xA7F9, 'M', u'œ'), (0xA7FA, 'V'), - (0xA82C, 'X'), + (0xA82D, 'X'), (0xA830, 'V'), (0xA83A, 'X'), (0xA840, 'V'), @@ -3955,11 +3954,11 @@ def _seg_37(): (0xA97D, 'X'), (0xA980, 'V'), (0xA9CE, 'X'), + (0xA9CF, 'V'), ] def _seg_38(): return [ - (0xA9CF, 'V'), (0xA9DA, 'X'), (0xA9DE, 'V'), (0xA9FF, 'X'), @@ -3989,7 +3988,9 @@ def _seg_38(): (0xAB5E, 'M', u'ɫ'), (0xAB5F, 'M', u'ꭒ'), (0xAB60, 'V'), - (0xAB68, 'X'), + (0xAB69, 'M', u'ʍ'), + (0xAB6A, 'V'), + (0xAB6C, 'X'), (0xAB70, 'M', u'Ꭰ'), (0xAB71, 'M', u'Ꭱ'), (0xAB72, 'M', u'Ꭲ'), @@ -4058,11 +4059,11 @@ def _seg_38(): (0xABB1, 'M', u'Ꮱ'), (0xABB2, 'M', u'Ꮲ'), (0xABB3, 'M', u'Ꮳ'), - (0xABB4, 'M', u'Ꮴ'), ] def _seg_39(): return [ + (0xABB4, 'M', u'Ꮴ'), (0xABB5, 'M', u'Ꮵ'), (0xABB6, 'M', u'Ꮶ'), (0xABB7, 'M', u'Ꮷ'), @@ -4162,11 +4163,11 @@ def _seg_39(): (0xF94C, 'M', u'樓'), (0xF94D, 'M', u'淚'), (0xF94E, 'M', u'漏'), - (0xF94F, 'M', u'累'), ] def _seg_40(): return [ + (0xF94F, 'M', u'累'), (0xF950, 'M', u'縷'), (0xF951, 'M', u'陋'), (0xF952, 'M', u'勒'), @@ -4266,11 +4267,11 @@ def _seg_40(): (0xF9B0, 'M', u'聆'), (0xF9B1, 'M', u'鈴'), (0xF9B2, 'M', u'零'), - (0xF9B3, 'M', u'靈'), ] def _seg_41(): return [ + (0xF9B3, 'M', u'靈'), (0xF9B4, 'M', u'領'), (0xF9B5, 'M', u'例'), (0xF9B6, 'M', u'禮'), @@ -4370,11 +4371,11 @@ def _seg_41(): (0xFA16, 'M', u'猪'), (0xFA17, 'M', u'益'), (0xFA18, 'M', u'礼'), - (0xFA19, 'M', u'神'), ] def _seg_42(): return [ + (0xFA19, 'M', u'神'), (0xFA1A, 'M', u'祥'), (0xFA1B, 'M', u'福'), (0xFA1C, 'M', u'靖'), @@ -4474,11 +4475,11 @@ def _seg_42(): (0xFA7F, 'M', u'奔'), (0xFA80, 'M', u'婢'), (0xFA81, 'M', u'嬨'), - (0xFA82, 'M', u'廒'), ] def _seg_43(): return [ + (0xFA82, 'M', u'廒'), (0xFA83, 'M', u'廙'), (0xFA84, 'M', u'彩'), (0xFA85, 'M', u'徭'), @@ -4578,11 +4579,11 @@ def _seg_43(): (0xFB14, 'M', u'մե'), (0xFB15, 'M', u'մի'), (0xFB16, 'M', u'վն'), - (0xFB17, 'M', u'մխ'), ] def _seg_44(): return [ + (0xFB17, 'M', u'մխ'), (0xFB18, 'X'), (0xFB1D, 'M', u'יִ'), (0xFB1E, 'V'), @@ -4682,11 +4683,11 @@ def _seg_44(): (0xFBEE, 'M', u'ئو'), (0xFBF0, 'M', u'ئۇ'), (0xFBF2, 'M', u'ئۆ'), - (0xFBF4, 'M', u'ئۈ'), ] def _seg_45(): return [ + (0xFBF4, 'M', u'ئۈ'), (0xFBF6, 'M', u'ئې'), (0xFBF9, 'M', u'ئى'), (0xFBFC, 'M', u'ی'), @@ -4786,11 +4787,11 @@ def _seg_45(): (0xFC5D, 'M', u'ىٰ'), (0xFC5E, '3', u' ٌّ'), (0xFC5F, '3', u' ٍّ'), - (0xFC60, '3', u' َّ'), ] def _seg_46(): return [ + (0xFC60, '3', u' َّ'), (0xFC61, '3', u' ُّ'), (0xFC62, '3', u' ِّ'), (0xFC63, '3', u' ّٰ'), @@ -4890,11 +4891,11 @@ def _seg_46(): (0xFCC1, 'M', u'فم'), (0xFCC2, 'M', u'قح'), (0xFCC3, 'M', u'قم'), - (0xFCC4, 'M', u'كج'), ] def _seg_47(): return [ + (0xFCC4, 'M', u'كج'), (0xFCC5, 'M', u'كح'), (0xFCC6, 'M', u'كخ'), (0xFCC7, 'M', u'كل'), @@ -4994,11 +4995,11 @@ def _seg_47(): (0xFD25, 'M', u'شج'), (0xFD26, 'M', u'شح'), (0xFD27, 'M', u'شخ'), - (0xFD28, 'M', u'شم'), ] def _seg_48(): return [ + (0xFD28, 'M', u'شم'), (0xFD29, 'M', u'شر'), (0xFD2A, 'M', u'سر'), (0xFD2B, 'M', u'صر'), @@ -5098,11 +5099,11 @@ def _seg_48(): (0xFDAC, 'M', u'لجي'), (0xFDAD, 'M', u'لمي'), (0xFDAE, 'M', u'يحي'), - (0xFDAF, 'M', u'يجي'), ] def _seg_49(): return [ + (0xFDAF, 'M', u'يجي'), (0xFDB0, 'M', u'يمي'), (0xFDB1, 'M', u'ممي'), (0xFDB2, 'M', u'قمي'), @@ -5202,11 +5203,11 @@ def _seg_49(): (0xFE64, '3', u'<'), (0xFE65, '3', u'>'), (0xFE66, '3', u'='), - (0xFE67, 'X'), ] def _seg_50(): return [ + (0xFE67, 'X'), (0xFE68, '3', u'\\'), (0xFE69, '3', u'$'), (0xFE6A, '3', u'%'), @@ -5306,11 +5307,11 @@ def _seg_50(): (0xFF21, 'M', u'a'), (0xFF22, 'M', u'b'), (0xFF23, 'M', u'c'), - (0xFF24, 'M', u'd'), ] def _seg_51(): return [ + (0xFF24, 'M', u'd'), (0xFF25, 'M', u'e'), (0xFF26, 'M', u'f'), (0xFF27, 'M', u'g'), @@ -5410,11 +5411,11 @@ def _seg_51(): (0xFF85, 'M', u'ナ'), (0xFF86, 'M', u'ニ'), (0xFF87, 'M', u'ヌ'), - (0xFF88, 'M', u'ネ'), ] def _seg_52(): return [ + (0xFF88, 'M', u'ネ'), (0xFF89, 'M', u'ノ'), (0xFF8A, 'M', u'ハ'), (0xFF8B, 'M', u'ヒ'), @@ -5514,11 +5515,11 @@ def _seg_52(): (0x10000, 'V'), (0x1000C, 'X'), (0x1000D, 'V'), - (0x10027, 'X'), ] def _seg_53(): return [ + (0x10027, 'X'), (0x10028, 'V'), (0x1003B, 'X'), (0x1003C, 'V'), @@ -5536,7 +5537,7 @@ def _seg_53(): (0x10137, 'V'), (0x1018F, 'X'), (0x10190, 'V'), - (0x1019C, 'X'), + (0x1019D, 'X'), (0x101A0, 'V'), (0x101A1, 'X'), (0x101D0, 'V'), @@ -5618,11 +5619,11 @@ def _seg_53(): (0x104BC, 'M', u'𐓤'), (0x104BD, 'M', u'𐓥'), (0x104BE, 'M', u'𐓦'), - (0x104BF, 'M', u'𐓧'), ] def _seg_54(): return [ + (0x104BF, 'M', u'𐓧'), (0x104C0, 'M', u'𐓨'), (0x104C1, 'M', u'𐓩'), (0x104C2, 'M', u'𐓪'), @@ -5722,11 +5723,11 @@ def _seg_54(): (0x10B9D, 'X'), (0x10BA9, 'V'), (0x10BB0, 'X'), - (0x10C00, 'V'), ] def _seg_55(): return [ + (0x10C00, 'V'), (0x10C49, 'X'), (0x10C80, 'M', u'𐳀'), (0x10C81, 'M', u'𐳁'), @@ -5788,10 +5789,18 @@ def _seg_55(): (0x10D3A, 'X'), (0x10E60, 'V'), (0x10E7F, 'X'), + (0x10E80, 'V'), + (0x10EAA, 'X'), + (0x10EAB, 'V'), + (0x10EAE, 'X'), + (0x10EB0, 'V'), + (0x10EB2, 'X'), (0x10F00, 'V'), (0x10F28, 'X'), (0x10F30, 'V'), (0x10F5A, 'X'), + (0x10FB0, 'V'), + (0x10FCC, 'X'), (0x10FE0, 'V'), (0x10FF7, 'X'), (0x11000, 'V'), @@ -5809,17 +5818,19 @@ def _seg_55(): (0x11100, 'V'), (0x11135, 'X'), (0x11136, 'V'), - (0x11147, 'X'), + (0x11148, 'X'), (0x11150, 'V'), (0x11177, 'X'), (0x11180, 'V'), - (0x111CE, 'X'), - (0x111D0, 'V'), (0x111E0, 'X'), (0x111E1, 'V'), (0x111F5, 'X'), (0x11200, 'V'), (0x11212, 'X'), + ] + +def _seg_56(): + return [ (0x11213, 'V'), (0x1123F, 'X'), (0x11280, 'V'), @@ -5827,10 +5838,6 @@ def _seg_55(): (0x11288, 'V'), (0x11289, 'X'), (0x1128A, 'V'), - ] - -def _seg_56(): - return [ (0x1128E, 'X'), (0x1128F, 'V'), (0x1129E, 'X'), @@ -5871,11 +5878,9 @@ def _seg_56(): (0x11370, 'V'), (0x11375, 'X'), (0x11400, 'V'), - (0x1145A, 'X'), - (0x1145B, 'V'), (0x1145C, 'X'), (0x1145D, 'V'), - (0x11460, 'X'), + (0x11462, 'X'), (0x11480, 'V'), (0x114C8, 'X'), (0x114D0, 'V'), @@ -5926,22 +5931,36 @@ def _seg_56(): (0x118B5, 'M', u'𑣕'), (0x118B6, 'M', u'𑣖'), (0x118B7, 'M', u'𑣗'), + ] + +def _seg_57(): + return [ (0x118B8, 'M', u'𑣘'), (0x118B9, 'M', u'𑣙'), (0x118BA, 'M', u'𑣚'), (0x118BB, 'M', u'𑣛'), (0x118BC, 'M', u'𑣜'), - ] - -def _seg_57(): - return [ (0x118BD, 'M', u'𑣝'), (0x118BE, 'M', u'𑣞'), (0x118BF, 'M', u'𑣟'), (0x118C0, 'V'), (0x118F3, 'X'), (0x118FF, 'V'), - (0x11900, 'X'), + (0x11907, 'X'), + (0x11909, 'V'), + (0x1190A, 'X'), + (0x1190C, 'V'), + (0x11914, 'X'), + (0x11915, 'V'), + (0x11917, 'X'), + (0x11918, 'V'), + (0x11936, 'X'), + (0x11937, 'V'), + (0x11939, 'X'), + (0x1193B, 'V'), + (0x11947, 'X'), + (0x11950, 'V'), + (0x1195A, 'X'), (0x119A0, 'V'), (0x119A8, 'X'), (0x119AA, 'V'), @@ -5996,6 +6015,8 @@ def _seg_57(): (0x11DAA, 'X'), (0x11EE0, 'V'), (0x11EF9, 'X'), + (0x11FB0, 'V'), + (0x11FB1, 'X'), (0x11FC0, 'V'), (0x11FF2, 'X'), (0x11FFF, 'V'), @@ -6014,6 +6035,10 @@ def _seg_57(): (0x16A39, 'X'), (0x16A40, 'V'), (0x16A5F, 'X'), + ] + +def _seg_58(): + return [ (0x16A60, 'V'), (0x16A6A, 'X'), (0x16A6E, 'V'), @@ -6035,10 +6060,6 @@ def _seg_57(): (0x16E40, 'M', u'𖹠'), (0x16E41, 'M', u'𖹡'), (0x16E42, 'M', u'𖹢'), - ] - -def _seg_58(): - return [ (0x16E43, 'M', u'𖹣'), (0x16E44, 'M', u'𖹤'), (0x16E45, 'M', u'𖹥'), @@ -6077,11 +6098,15 @@ def _seg_58(): (0x16F8F, 'V'), (0x16FA0, 'X'), (0x16FE0, 'V'), - (0x16FE4, 'X'), + (0x16FE5, 'X'), + (0x16FF0, 'V'), + (0x16FF2, 'X'), (0x17000, 'V'), (0x187F8, 'X'), (0x18800, 'V'), - (0x18AF3, 'X'), + (0x18CD6, 'X'), + (0x18D00, 'V'), + (0x18D09, 'X'), (0x1B000, 'V'), (0x1B11F, 'X'), (0x1B150, 'V'), @@ -6114,6 +6139,10 @@ def _seg_58(): (0x1D163, 'M', u'𝅘𝅥𝅱'), (0x1D164, 'M', u'𝅘𝅥𝅲'), (0x1D165, 'V'), + ] + +def _seg_59(): + return [ (0x1D173, 'X'), (0x1D17B, 'V'), (0x1D1BB, 'M', u'𝆹𝅥'), @@ -6139,10 +6168,6 @@ def _seg_58(): (0x1D404, 'M', u'e'), (0x1D405, 'M', u'f'), (0x1D406, 'M', u'g'), - ] - -def _seg_59(): - return [ (0x1D407, 'M', u'h'), (0x1D408, 'M', u'i'), (0x1D409, 'M', u'j'), @@ -6218,6 +6243,10 @@ def _seg_59(): (0x1D44F, 'M', u'b'), (0x1D450, 'M', u'c'), (0x1D451, 'M', u'd'), + ] + +def _seg_60(): + return [ (0x1D452, 'M', u'e'), (0x1D453, 'M', u'f'), (0x1D454, 'M', u'g'), @@ -6243,10 +6272,6 @@ def _seg_59(): (0x1D468, 'M', u'a'), (0x1D469, 'M', u'b'), (0x1D46A, 'M', u'c'), - ] - -def _seg_60(): - return [ (0x1D46B, 'M', u'd'), (0x1D46C, 'M', u'e'), (0x1D46D, 'M', u'f'), @@ -6322,6 +6347,10 @@ def _seg_60(): (0x1D4B6, 'M', u'a'), (0x1D4B7, 'M', u'b'), (0x1D4B8, 'M', u'c'), + ] + +def _seg_61(): + return [ (0x1D4B9, 'M', u'd'), (0x1D4BA, 'X'), (0x1D4BB, 'M', u'f'), @@ -6347,10 +6376,6 @@ def _seg_60(): (0x1D4CF, 'M', u'z'), (0x1D4D0, 'M', u'a'), (0x1D4D1, 'M', u'b'), - ] - -def _seg_61(): - return [ (0x1D4D2, 'M', u'c'), (0x1D4D3, 'M', u'd'), (0x1D4D4, 'M', u'e'), @@ -6426,6 +6451,10 @@ def _seg_61(): (0x1D51B, 'M', u'x'), (0x1D51C, 'M', u'y'), (0x1D51D, 'X'), + ] + +def _seg_62(): + return [ (0x1D51E, 'M', u'a'), (0x1D51F, 'M', u'b'), (0x1D520, 'M', u'c'), @@ -6451,10 +6480,6 @@ def _seg_61(): (0x1D534, 'M', u'w'), (0x1D535, 'M', u'x'), (0x1D536, 'M', u'y'), - ] - -def _seg_62(): - return [ (0x1D537, 'M', u'z'), (0x1D538, 'M', u'a'), (0x1D539, 'M', u'b'), @@ -6530,6 +6555,10 @@ def _seg_62(): (0x1D581, 'M', u'v'), (0x1D582, 'M', u'w'), (0x1D583, 'M', u'x'), + ] + +def _seg_63(): + return [ (0x1D584, 'M', u'y'), (0x1D585, 'M', u'z'), (0x1D586, 'M', u'a'), @@ -6555,10 +6584,6 @@ def _seg_62(): (0x1D59A, 'M', u'u'), (0x1D59B, 'M', u'v'), (0x1D59C, 'M', u'w'), - ] - -def _seg_63(): - return [ (0x1D59D, 'M', u'x'), (0x1D59E, 'M', u'y'), (0x1D59F, 'M', u'z'), @@ -6634,6 +6659,10 @@ def _seg_63(): (0x1D5E5, 'M', u'r'), (0x1D5E6, 'M', u's'), (0x1D5E7, 'M', u't'), + ] + +def _seg_64(): + return [ (0x1D5E8, 'M', u'u'), (0x1D5E9, 'M', u'v'), (0x1D5EA, 'M', u'w'), @@ -6659,10 +6688,6 @@ def _seg_63(): (0x1D5FE, 'M', u'q'), (0x1D5FF, 'M', u'r'), (0x1D600, 'M', u's'), - ] - -def _seg_64(): - return [ (0x1D601, 'M', u't'), (0x1D602, 'M', u'u'), (0x1D603, 'M', u'v'), @@ -6738,6 +6763,10 @@ def _seg_64(): (0x1D649, 'M', u'n'), (0x1D64A, 'M', u'o'), (0x1D64B, 'M', u'p'), + ] + +def _seg_65(): + return [ (0x1D64C, 'M', u'q'), (0x1D64D, 'M', u'r'), (0x1D64E, 'M', u's'), @@ -6763,10 +6792,6 @@ def _seg_64(): (0x1D662, 'M', u'm'), (0x1D663, 'M', u'n'), (0x1D664, 'M', u'o'), - ] - -def _seg_65(): - return [ (0x1D665, 'M', u'p'), (0x1D666, 'M', u'q'), (0x1D667, 'M', u'r'), @@ -6842,6 +6867,10 @@ def _seg_65(): (0x1D6AE, 'M', u'η'), (0x1D6AF, 'M', u'θ'), (0x1D6B0, 'M', u'ι'), + ] + +def _seg_66(): + return [ (0x1D6B1, 'M', u'κ'), (0x1D6B2, 'M', u'λ'), (0x1D6B3, 'M', u'μ'), @@ -6867,10 +6896,6 @@ def _seg_65(): (0x1D6C7, 'M', u'ζ'), (0x1D6C8, 'M', u'η'), (0x1D6C9, 'M', u'θ'), - ] - -def _seg_66(): - return [ (0x1D6CA, 'M', u'ι'), (0x1D6CB, 'M', u'κ'), (0x1D6CC, 'M', u'λ'), @@ -6946,6 +6971,10 @@ def _seg_66(): (0x1D714, 'M', u'ω'), (0x1D715, 'M', u'∂'), (0x1D716, 'M', u'ε'), + ] + +def _seg_67(): + return [ (0x1D717, 'M', u'θ'), (0x1D718, 'M', u'κ'), (0x1D719, 'M', u'φ'), @@ -6971,10 +7000,6 @@ def _seg_66(): (0x1D72D, 'M', u'θ'), (0x1D72E, 'M', u'σ'), (0x1D72F, 'M', u'τ'), - ] - -def _seg_67(): - return [ (0x1D730, 'M', u'υ'), (0x1D731, 'M', u'φ'), (0x1D732, 'M', u'χ'), @@ -7050,6 +7075,10 @@ def _seg_67(): (0x1D779, 'M', u'κ'), (0x1D77A, 'M', u'λ'), (0x1D77B, 'M', u'μ'), + ] + +def _seg_68(): + return [ (0x1D77C, 'M', u'ν'), (0x1D77D, 'M', u'ξ'), (0x1D77E, 'M', u'ο'), @@ -7075,10 +7104,6 @@ def _seg_67(): (0x1D793, 'M', u'δ'), (0x1D794, 'M', u'ε'), (0x1D795, 'M', u'ζ'), - ] - -def _seg_68(): - return [ (0x1D796, 'M', u'η'), (0x1D797, 'M', u'θ'), (0x1D798, 'M', u'ι'), @@ -7154,6 +7179,10 @@ def _seg_68(): (0x1D7E1, 'M', u'9'), (0x1D7E2, 'M', u'0'), (0x1D7E3, 'M', u'1'), + ] + +def _seg_69(): + return [ (0x1D7E4, 'M', u'2'), (0x1D7E5, 'M', u'3'), (0x1D7E6, 'M', u'4'), @@ -7179,10 +7208,6 @@ def _seg_68(): (0x1D7FA, 'M', u'4'), (0x1D7FB, 'M', u'5'), (0x1D7FC, 'M', u'6'), - ] - -def _seg_69(): - return [ (0x1D7FD, 'M', u'7'), (0x1D7FE, 'M', u'8'), (0x1D7FF, 'M', u'9'), @@ -7258,6 +7283,10 @@ def _seg_69(): (0x1E95A, 'X'), (0x1E95E, 'V'), (0x1E960, 'X'), + ] + +def _seg_70(): + return [ (0x1EC71, 'V'), (0x1ECB5, 'X'), (0x1ED01, 'V'), @@ -7283,10 +7312,6 @@ def _seg_69(): (0x1EE12, 'M', u'ق'), (0x1EE13, 'M', u'ر'), (0x1EE14, 'M', u'ش'), - ] - -def _seg_70(): - return [ (0x1EE15, 'M', u'ت'), (0x1EE16, 'M', u'ث'), (0x1EE17, 'M', u'خ'), @@ -7362,6 +7387,10 @@ def _seg_70(): (0x1EE68, 'M', u'ط'), (0x1EE69, 'M', u'ي'), (0x1EE6A, 'M', u'ك'), + ] + +def _seg_71(): + return [ (0x1EE6B, 'X'), (0x1EE6C, 'M', u'م'), (0x1EE6D, 'M', u'ن'), @@ -7387,10 +7416,6 @@ def _seg_70(): (0x1EE81, 'M', u'ب'), (0x1EE82, 'M', u'ج'), (0x1EE83, 'M', u'د'), - ] - -def _seg_71(): - return [ (0x1EE84, 'M', u'ه'), (0x1EE85, 'M', u'و'), (0x1EE86, 'M', u'ز'), @@ -7466,10 +7491,13 @@ def _seg_71(): (0x1F106, '3', u'5,'), (0x1F107, '3', u'6,'), (0x1F108, '3', u'7,'), + ] + +def _seg_72(): + return [ (0x1F109, '3', u'8,'), (0x1F10A, '3', u'9,'), (0x1F10B, 'V'), - (0x1F10D, 'X'), (0x1F110, '3', u'(a)'), (0x1F111, '3', u'(b)'), (0x1F112, '3', u'(c)'), @@ -7491,10 +7519,6 @@ def _seg_71(): (0x1F122, '3', u'(s)'), (0x1F123, '3', u'(t)'), (0x1F124, '3', u'(u)'), - ] - -def _seg_72(): - return [ (0x1F125, '3', u'(v)'), (0x1F126, '3', u'(w)'), (0x1F127, '3', u'(x)'), @@ -7542,11 +7566,10 @@ def _seg_72(): (0x1F16A, 'M', u'mc'), (0x1F16B, 'M', u'md'), (0x1F16C, 'M', u'mr'), - (0x1F16D, 'X'), - (0x1F170, 'V'), + (0x1F16D, 'V'), (0x1F190, 'M', u'dj'), (0x1F191, 'V'), - (0x1F1AD, 'X'), + (0x1F1AE, 'X'), (0x1F1E6, 'V'), (0x1F200, 'M', u'ほか'), (0x1F201, 'M', u'ココ'), @@ -7572,6 +7595,10 @@ def _seg_72(): (0x1F221, 'M', u'終'), (0x1F222, 'M', u'生'), (0x1F223, 'M', u'販'), + ] + +def _seg_73(): + return [ (0x1F224, 'M', u'声'), (0x1F225, 'M', u'吹'), (0x1F226, 'M', u'演'), @@ -7595,10 +7622,6 @@ def _seg_72(): (0x1F238, 'M', u'申'), (0x1F239, 'M', u'割'), (0x1F23A, 'M', u'営'), - ] - -def _seg_73(): - return [ (0x1F23B, 'M', u'配'), (0x1F23C, 'X'), (0x1F240, 'M', u'〔本〕'), @@ -7617,11 +7640,11 @@ def _seg_73(): (0x1F260, 'V'), (0x1F266, 'X'), (0x1F300, 'V'), - (0x1F6D6, 'X'), + (0x1F6D8, 'X'), (0x1F6E0, 'V'), (0x1F6ED, 'X'), (0x1F6F0, 'V'), - (0x1F6FB, 'X'), + (0x1F6FD, 'X'), (0x1F700, 'V'), (0x1F774, 'X'), (0x1F780, 'V'), @@ -7638,32 +7661,51 @@ def _seg_73(): (0x1F888, 'X'), (0x1F890, 'V'), (0x1F8AE, 'X'), + (0x1F8B0, 'V'), + (0x1F8B2, 'X'), (0x1F900, 'V'), - (0x1F90C, 'X'), - (0x1F90D, 'V'), - (0x1F972, 'X'), - (0x1F973, 'V'), - (0x1F977, 'X'), + (0x1F979, 'X'), (0x1F97A, 'V'), - (0x1F9A3, 'X'), - (0x1F9A5, 'V'), - (0x1F9AB, 'X'), - (0x1F9AE, 'V'), - (0x1F9CB, 'X'), + (0x1F9CC, 'X'), (0x1F9CD, 'V'), (0x1FA54, 'X'), (0x1FA60, 'V'), (0x1FA6E, 'X'), (0x1FA70, 'V'), - (0x1FA74, 'X'), + (0x1FA75, 'X'), (0x1FA78, 'V'), (0x1FA7B, 'X'), (0x1FA80, 'V'), - (0x1FA83, 'X'), + (0x1FA87, 'X'), (0x1FA90, 'V'), - (0x1FA96, 'X'), + (0x1FAA9, 'X'), + (0x1FAB0, 'V'), + (0x1FAB7, 'X'), + (0x1FAC0, 'V'), + (0x1FAC3, 'X'), + (0x1FAD0, 'V'), + (0x1FAD7, 'X'), + (0x1FB00, 'V'), + (0x1FB93, 'X'), + (0x1FB94, 'V'), + (0x1FBCB, 'X'), + (0x1FBF0, 'M', u'0'), + (0x1FBF1, 'M', u'1'), + (0x1FBF2, 'M', u'2'), + (0x1FBF3, 'M', u'3'), + (0x1FBF4, 'M', u'4'), + (0x1FBF5, 'M', u'5'), + (0x1FBF6, 'M', u'6'), + (0x1FBF7, 'M', u'7'), + (0x1FBF8, 'M', u'8'), + (0x1FBF9, 'M', u'9'), + ] + +def _seg_74(): + return [ + (0x1FBFA, 'X'), (0x20000, 'V'), - (0x2A6D7, 'X'), + (0x2A6DE, 'X'), (0x2A700, 'V'), (0x2B735, 'X'), (0x2B740, 'V'), @@ -7699,10 +7741,6 @@ def _seg_73(): (0x2F818, 'M', u'冤'), (0x2F819, 'M', u'仌'), (0x2F81A, 'M', u'冬'), - ] - -def _seg_74(): - return [ (0x2F81B, 'M', u'况'), (0x2F81C, 'M', u'𩇟'), (0x2F81D, 'M', u'凵'), @@ -7765,6 +7803,10 @@ def _seg_74(): (0x2F859, 'M', u'𡓤'), (0x2F85A, 'M', u'売'), (0x2F85B, 'M', u'壷'), + ] + +def _seg_75(): + return [ (0x2F85C, 'M', u'夆'), (0x2F85D, 'M', u'多'), (0x2F85E, 'M', u'夢'), @@ -7803,10 +7845,6 @@ def _seg_74(): (0x2F880, 'M', u'嵼'), (0x2F881, 'M', u'巡'), (0x2F882, 'M', u'巢'), - ] - -def _seg_75(): - return [ (0x2F883, 'M', u'㠯'), (0x2F884, 'M', u'巽'), (0x2F885, 'M', u'帨'), @@ -7869,6 +7907,10 @@ def _seg_75(): (0x2F8C0, 'M', u'揅'), (0x2F8C1, 'M', u'掩'), (0x2F8C2, 'M', u'㨮'), + ] + +def _seg_76(): + return [ (0x2F8C3, 'M', u'摩'), (0x2F8C4, 'M', u'摾'), (0x2F8C5, 'M', u'撝'), @@ -7907,10 +7949,6 @@ def _seg_75(): (0x2F8E6, 'M', u'椔'), (0x2F8E7, 'M', u'㮝'), (0x2F8E8, 'M', u'楂'), - ] - -def _seg_76(): - return [ (0x2F8E9, 'M', u'榣'), (0x2F8EA, 'M', u'槪'), (0x2F8EB, 'M', u'檨'), @@ -7973,6 +8011,10 @@ def _seg_76(): (0x2F924, 'M', u'犀'), (0x2F925, 'M', u'犕'), (0x2F926, 'M', u'𤜵'), + ] + +def _seg_77(): + return [ (0x2F927, 'M', u'𤠔'), (0x2F928, 'M', u'獺'), (0x2F929, 'M', u'王'), @@ -8011,10 +8053,6 @@ def _seg_76(): (0x2F94C, 'M', u'䂖'), (0x2F94D, 'M', u'𥐝'), (0x2F94E, 'M', u'硎'), - ] - -def _seg_77(): - return [ (0x2F94F, 'M', u'碌'), (0x2F950, 'M', u'磌'), (0x2F951, 'M', u'䃣'), @@ -8077,6 +8115,10 @@ def _seg_77(): (0x2F98B, 'M', u'舁'), (0x2F98C, 'M', u'舄'), (0x2F98D, 'M', u'辞'), + ] + +def _seg_78(): + return [ (0x2F98E, 'M', u'䑫'), (0x2F98F, 'M', u'芑'), (0x2F990, 'M', u'芋'), @@ -8115,10 +8157,6 @@ def _seg_77(): (0x2F9B1, 'M', u'𧃒'), (0x2F9B2, 'M', u'䕫'), (0x2F9B3, 'M', u'虐'), - ] - -def _seg_78(): - return [ (0x2F9B4, 'M', u'虜'), (0x2F9B5, 'M', u'虧'), (0x2F9B6, 'M', u'虩'), @@ -8181,6 +8219,10 @@ def _seg_78(): (0x2F9EF, 'M', u'䦕'), (0x2F9F0, 'M', u'閷'), (0x2F9F1, 'M', u'𨵷'), + ] + +def _seg_79(): + return [ (0x2F9F2, 'M', u'䧦'), (0x2F9F3, 'M', u'雃'), (0x2F9F4, 'M', u'嶲'), @@ -8219,16 +8261,14 @@ def _seg_78(): (0x2FA16, 'M', u'䵖'), (0x2FA17, 'M', u'黹'), (0x2FA18, 'M', u'黾'), - ] - -def _seg_79(): - return [ (0x2FA19, 'M', u'鼅'), (0x2FA1A, 'M', u'鼏'), (0x2FA1B, 'M', u'鼖'), (0x2FA1C, 'M', u'鼻'), (0x2FA1D, 'M', u'𪘀'), (0x2FA1E, 'X'), + (0x30000, 'V'), + (0x3134B, 'X'), (0xE0100, 'I'), (0xE01F0, 'X'), ] diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py index 5161d14..4d99857 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py @@ -18,10 +18,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "20.3" +__version__ = "20.4" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" +__license__ = "BSD-2-Clause or Apache-2.0" __copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py index a145f7e..e54bd4e 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py @@ -5,9 +5,9 @@ from __future__ import absolute_import, division, print_function import sys -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import Any, Dict, Tuple, Type diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py index 945b39c..2846133 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py @@ -18,22 +18,31 @@ curious maintainer can reach here to read this. In packaging, all static-typing related imports should be guarded as follows: - from pip._vendor.packaging._typing import MYPY_CHECK_RUNNING + from pip._vendor.packaging._typing import TYPE_CHECKING - if MYPY_CHECK_RUNNING: + if TYPE_CHECKING: from typing import ... Ref: https://github.com/python/mypy/issues/3216 """ -MYPY_CHECK_RUNNING = False +__all__ = ["TYPE_CHECKING", "cast"] -if MYPY_CHECK_RUNNING: # pragma: no cover - import typing - - cast = typing.cast +# The TYPE_CHECKING constant defined by the typing module is False at runtime +# but True while type checking. +if False: # pragma: no cover + from typing import TYPE_CHECKING else: - # typing's cast() is needed at runtime, but we don't want to import typing. - # Thus, we use a dummy no-op version, which we tell mypy to ignore. - def cast(type_, value): # type: ignore + TYPE_CHECKING = False + +# typing's cast syntax requires calling typing.cast at runtime, but we don't +# want to import typing at runtime. Here, we inform the type checkers that +# we're importing `typing.cast` as `cast` and re-implement typing.cast's +# runtime behavior in a block that is ignored by type checkers. +if TYPE_CHECKING: # pragma: no cover + # not executed at runtime + from typing import cast +else: + # executed at runtime + def cast(type_, value): # noqa return value diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py index b24f8ed..ed642b0 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py @@ -13,10 +13,10 @@ from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString from pip._vendor.pyparsing import Literal as L # noqa from ._compat import string_types -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING from .specifiers import Specifier, InvalidSpecifier -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import Any, Callable, Dict, List, Optional, Tuple, Union Operator = Callable[[str, str], bool] diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py index 1e32a93..5e64101 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py @@ -11,11 +11,11 @@ from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine from pip._vendor.pyparsing import Literal as L # noqa from pip._vendor.six.moves.urllib import parse as urlparse -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import List diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py index 9498748..fe09bb1 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py @@ -9,10 +9,11 @@ import itertools import re from ._compat import string_types, with_metaclass -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING +from .utils import canonicalize_version from .version import Version, LegacyVersion, parse -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import ( List, Dict, @@ -132,9 +133,14 @@ class _IndividualSpecifier(BaseSpecifier): # type: () -> str return "{0}{1}".format(*self._spec) + @property + def _canonical_spec(self): + # type: () -> Tuple[str, Union[Version, str]] + return self._spec[0], canonicalize_version(self._spec[1]) + def __hash__(self): # type: () -> int - return hash(self._spec) + return hash(self._canonical_spec) def __eq__(self, other): # type: (object) -> bool @@ -146,7 +152,7 @@ class _IndividualSpecifier(BaseSpecifier): elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec def __ne__(self, other): # type: (object) -> bool @@ -510,12 +516,20 @@ class Specifier(_IndividualSpecifier): @_require_version_compare def _compare_less_than_equal(self, prospective, spec): # type: (ParsedVersion, str) -> bool - return prospective <= Version(spec) + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare def _compare_greater_than_equal(self, prospective, spec): # type: (ParsedVersion, str) -> bool - return prospective >= Version(spec) + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare def _compare_less_than(self, prospective, spec_str): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py index 300faab..9064910 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py @@ -22,9 +22,9 @@ import sys import sysconfig import warnings -from ._typing import MYPY_CHECK_RUNNING, cast +from ._typing import TYPE_CHECKING, cast -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import ( Dict, FrozenSet, @@ -58,6 +58,12 @@ _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 class Tag(object): + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ __slots__ = ["_interpreter", "_abi", "_platform"] @@ -108,6 +114,12 @@ class Tag(object): def parse_tag(tag): # type: (str) -> FrozenSet[Tag] + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -541,7 +553,7 @@ class _ELFFileHeader(object): def unpack(fmt): # type: (str) -> int try: - result, = struct.unpack( + (result,) = struct.unpack( fmt, file.read(struct.calcsize(fmt)) ) # type: (int, ) except struct.error: diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py index 44f1bf9..19579c1 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py @@ -5,19 +5,22 @@ from __future__ import absolute_import, division, print_function import re -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING, cast from .version import InvalidVersion, Version -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import Union +if TYPE_CHECKING: # pragma: no cover + from typing import NewType, Union + + NormalizedName = NewType("NormalizedName", str) _canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): - # type: (str) -> str + # type: (str) -> NormalizedName # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast("NormalizedName", value) def canonicalize_version(_version): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py index f39a2a1..00371e8 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py @@ -8,9 +8,9 @@ import itertools import re from ._structures import Infinity, NegativeInfinity -from ._typing import MYPY_CHECK_RUNNING +from ._typing import TYPE_CHECKING -if MYPY_CHECK_RUNNING: # pragma: no cover +if TYPE_CHECKING: # pragma: no cover from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union from ._structures import InfinityType, NegativeInfinityType diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/requests/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/requests/__init__.py index e47bcb2..517458b 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/requests/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/requests/__init__.py @@ -90,18 +90,29 @@ except (AssertionError, ValueError): "version!".format(urllib3.__version__, chardet.__version__), RequestsDependencyWarning) -# Attempt to enable urllib3's SNI support, if possible -from pip._internal.utils.compat import WINDOWS -if not WINDOWS: +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. +try: + # Note: This logic prevents upgrading cryptography on Windows, if imported + # as part of pip. + from pip._internal.utils.compat import WINDOWS + if not WINDOWS: + raise ImportError("pip internals: don't import cryptography on Windows") try: + import ssl + except ImportError: + ssl = None + + if not getattr(ssl, "HAS_SNI", False): from pip._vendor.urllib3.contrib import pyopenssl pyopenssl.inject_into_urllib3() # Check cryptography version from cryptography import __version__ as cryptography_version _check_cryptography(cryptography_version) - except ImportError: - pass +except ImportError: + pass # urllib3's DependencyWarnings should be silenced. from pip._vendor.urllib3.exceptions import DependencyWarning diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/requests/__version__.py b/venv/lib/python3.8/site-packages/pip/_vendor/requests/__version__.py index b9e7df4..531e26c 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/requests/__version__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/requests/__version__.py @@ -5,8 +5,8 @@ __title__ = 'requests' __description__ = 'Python HTTP for Humans.' __url__ = 'https://requests.readthedocs.io' -__version__ = '2.23.0' -__build__ = 0x022300 +__version__ = '2.24.0' +__build__ = 0x022400 __author__ = 'Kenneth Reitz' __author_email__ = 'me@kennethreitz.org' __license__ = 'Apache 2.0' diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/requests/exceptions.py b/venv/lib/python3.8/site-packages/pip/_vendor/requests/exceptions.py index a91e1fd..9ef9e6e 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/requests/exceptions.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/requests/exceptions.py @@ -94,11 +94,11 @@ class ChunkedEncodingError(RequestException): class ContentDecodingError(RequestException, BaseHTTPError): - """Failed to decode response content""" + """Failed to decode response content.""" class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed""" + """The content for this response was already consumed.""" class RetryError(RequestException): @@ -106,21 +106,18 @@ class RetryError(RequestException): class UnrewindableBodyError(RequestException): - """Requests encountered an error when trying to rewind a body""" + """Requests encountered an error when trying to rewind a body.""" # Warnings class RequestsWarning(Warning): """Base warning for Requests.""" - pass class FileModeWarning(RequestsWarning, DeprecationWarning): """A file was opened in text mode, but Requests determined its binary length.""" - pass class RequestsDependencyWarning(RequestsWarning): """An imported dependency doesn't match the expected version range.""" - pass diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/requests/models.py b/venv/lib/python3.8/site-packages/pip/_vendor/requests/models.py index 8a3085d..015e715 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/requests/models.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/requests/models.py @@ -473,12 +473,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): not isinstance(data, (basestring, list, tuple, Mapping)) ]) - try: - length = super_len(data) - except (TypeError, AttributeError, UnsupportedOperation): - length = None - if is_stream: + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + body = data if getattr(body, 'tell', None) is not None: @@ -916,7 +916,7 @@ class Response(object): return l def raise_for_status(self): - """Raises stored :class:`HTTPError`, if one occurred.""" + """Raises :class:`HTTPError`, if one occurred.""" http_error_msg = '' if isinstance(self.reason, bytes): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/requests/sessions.py b/venv/lib/python3.8/site-packages/pip/_vendor/requests/sessions.py index 2845880..e8e2d60 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/requests/sessions.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/requests/sessions.py @@ -658,11 +658,13 @@ class Session(SessionRedirectMixin): extract_cookies_to_jar(self.cookies, request, r.raw) - # Redirect resolving generator. - gen = self.resolve_redirects(r, request, **kwargs) - # Resolve redirects if allowed. - history = [resp for resp in gen] if allow_redirects else [] + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] # Shuffle things around if there's history. if history: diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/__init__.py index aaba5b3..3b44454 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/__init__.py @@ -11,7 +11,7 @@ __all__ = [ "ResolutionTooDeep", ] -__version__ = "0.3.0" +__version__ = "0.4.0" from .providers import AbstractProvider, AbstractResolver diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/compat/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/compat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py new file mode 100644 index 0000000..366cc5e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py @@ -0,0 +1,6 @@ +__all__ = ["Sequence"] + +try: + from collections.abc import Sequence +except ImportError: + from collections import Sequence diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/providers.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/providers.py index db16821..68b7290 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/providers.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/providers.py @@ -27,7 +27,7 @@ class AbstractProvider(object): * `requirement` specifies a requirement contributing to the current candidate list - * `parent` specifies the candidate that provids (dependend on) the + * `parent` specifies the candidate that provides (dependend on) the requirement, or `None` to indicate a root requirement. The preference could depend on a various of issues, including (not @@ -48,23 +48,28 @@ class AbstractProvider(object): """ raise NotImplementedError - def find_matches(self, requirement): - """Find all possible candidates that satisfy a requirement. + def find_matches(self, requirements): + """Find all possible candidates that satisfy the given requirements. - This should try to get candidates based on the requirement's type. + This should try to get candidates based on the requirements' types. For VCS, local, and archive requirements, the one-and-only match is returned, and for a "named" requirement, the index(es) should be consulted to find concrete candidates for this requirement. - The returned candidates should be sorted by reversed preference, e.g. - the most preferred should be LAST. This is done so list-popping can be - as efficient as possible. + :param requirements: A collection of requirements which all of the the + returned candidates must match. All requirements are guaranteed to + have the same identifier. The collection is never empty. + :returns: An iterable that orders candidates by preference, e.g. the + most preferred candidate should come first. """ raise NotImplementedError def is_satisfied_by(self, requirement, candidate): """Whether the given requirement can be satisfied by a candidate. + The candidate is guarenteed to have been generated from the + requirement. + A boolean should be returned to indicate whether `candidate` is a viable solution to the requirement. """ @@ -92,30 +97,13 @@ class AbstractResolver(object): def resolve(self, requirements, **kwargs): """Take a collection of constraints, spit out the resolution result. - Parameters - ---------- - requirements : Collection - A collection of constraints - kwargs : optional - Additional keyword arguments that subclasses may accept. + This returns a representation of the final resolution state, with one + guarenteed attribute ``mapping`` that contains resolved candidates as + values. The keys are their respective identifiers. - Raises - ------ - self.base_exception - Any raised exception is guaranteed to be a subclass of - self.base_exception. The string representation of an exception - should be human readable and provide context for why it occurred. + :param requirements: A collection of constraints. + :param kwargs: Additional keyword arguments that subclasses may accept. - Returns - ------- - retval : object - A representation of the final resolution state. It can be any object - with a `mapping` attribute that is a Mapping. Other attributes can - be used to provide resolver-specific information. - - The `mapping` attribute MUST be key-value pair is an identifier of a - requirement (as returned by the provider's `identify` method) mapped - to the resolved candidate (chosen from the return value of the - provider's `find_matches` method). + :raises: ``self.base_exception`` or its subclass. """ raise NotImplementedError diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/reporters.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/reporters.py index c7e9e88..a0a2a45 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/reporters.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/reporters.py @@ -23,12 +23,18 @@ class BaseReporter(object): """Called before the resolution ends successfully. """ - def adding_requirement(self, requirement): - """Called when the resolver adds a new requirement into the resolve criteria. + def adding_requirement(self, requirement, parent): + """Called when adding a new requirement into the resolve criteria. + + :param requirement: The additional requirement to be applied to filter + the available candidaites. + :param parent: The candidate that requires ``requirement`` as a + dependency, or None if ``requirement`` is one of the root + requirements passed in from ``Resolver.resolve()``. """ def backtracking(self, candidate): - """Called when the resolver rejects a candidate during backtracking. + """Called when rejecting a candidate during backtracking. """ def pinning(self, candidate): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py index b51d337..4497f97 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py @@ -1,5 +1,6 @@ import collections +from .compat import collections_abc from .providers import AbstractResolver from .structs import DirectedGraph @@ -68,16 +69,18 @@ class Criterion(object): def __repr__(self): requirements = ", ".join( - "{!r} from {!r}".format(req, parent) + "({!r}, via={!r})".format(req, parent) for req, parent in self.information ) - return "".format(requirements) + return "Criterion({})".format(requirements) @classmethod def from_requirement(cls, provider, requirement, parent): """Build an instance from a requirement. """ - candidates = provider.find_matches(requirement) + candidates = provider.find_matches([requirement]) + if not isinstance(candidates, collections_abc.Sequence): + candidates = list(candidates) criterion = cls( candidates=candidates, information=[RequirementInformation(requirement, parent)], @@ -98,11 +101,9 @@ class Criterion(object): """ infos = list(self.information) infos.append(RequirementInformation(requirement, parent)) - candidates = [ - c - for c in self.candidates - if provider.is_satisfied_by(requirement, c) - ] + candidates = provider.find_matches([r for r, _ in infos]) + if not isinstance(candidates, collections_abc.Sequence): + candidates = list(candidates) criterion = type(self)(candidates, infos, list(self.incompatibilities)) if not candidates: raise RequirementsConflicted(criterion) @@ -179,7 +180,7 @@ class Resolution(object): self._states.append(state) def _merge_into_criterion(self, requirement, parent): - self._r.adding_requirement(requirement) + self._r.adding_requirement(requirement, parent) name = self._p.identify(requirement) try: crit = self.state.criteria[name] @@ -218,13 +219,24 @@ class Resolution(object): def _attempt_to_pin_criterion(self, name, criterion): causes = [] - for candidate in reversed(criterion.candidates): + for candidate in criterion.candidates: try: criteria = self._get_criteria_to_update(candidate) except RequirementsConflicted as e: causes.append(e.criterion) continue + # Check the newly-pinned candidate actually works. This should + # always pass under normal circumstances, but in the case of a + # faulty provider, we will raise an error to notify the implementer + # to fix find_matches() and/or is_satisfied_by(). + satisfied = all( + self._p.is_satisfied_by(r, candidate) + for r in criterion.iter_requirement() + ) + if not satisfied: + raise InconsistentCandidate(candidate, criterion) + # Put newly-pinned candidate at the end. This is essential because # backtracking looks at this mapping to get the last pin. self._r.pinning(candidate) @@ -232,13 +244,6 @@ class Resolution(object): self.state.mapping[name] = candidate self.state.criteria.update(criteria) - # Check the newly-pinned candidate actually works. This should - # always pass under normal circumstances, but in the case of a - # faulty provider, we will raise an error to notify the implementer - # to fix find_matches() and/or is_satisfied_by(). - if not self._is_current_pin_satisfying(name, criterion): - raise InconsistentCandidate(candidate, criterion) - return [] # All candidates tried, nothing works. This criterion is a dead @@ -246,23 +251,32 @@ class Resolution(object): return causes def _backtrack(self): - # We need at least 3 states here: - # (a) One known not working, to drop. - # (b) One to backtrack to. - # (c) One to restore state (b) to its state prior to candidate-pinning, - # so we can pin another one instead. - while len(self._states) >= 3: - del self._states[-1] + # Drop the current state, it's known not to work. + del self._states[-1] - # Retract the last candidate pin, and create a new (b). - name, candidate = self._states.pop().mapping.popitem() + # We need at least 2 states here: + # (a) One to backtrack to. + # (b) One to restore state (a) to its state prior to candidate-pinning, + # so we can pin another one instead. + + while len(self._states) >= 2: + # Retract the last candidate pin. + prev_state = self._states.pop() + try: + name, candidate = prev_state.mapping.popitem() + except KeyError: + continue self._r.backtracking(candidate) + + # Create a new state to work on, with the newly known not-working + # candidate excluded. self._push_new_state() # Mark the retracted candidate as incompatible. criterion = self.state.criteria[name].excluded_of(candidate) if criterion is None: # This state still does not work. Try the still previous state. + del self._states[-1] continue self.state.criteria[name] = criterion diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/six.py b/venv/lib/python3.8/site-packages/pip/_vendor/six.py index 5fe9f8e..83f6978 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/six.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/six.py @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.14.0" +__version__ = "1.15.0" # Useful for very coarse version differentiation. @@ -890,12 +890,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding='utf-8', errors='strict'): @@ -909,12 +908,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml.py deleted file mode 100644 index dac3988..0000000 --- a/venv/lib/python3.8/site-packages/pip/_vendor/toml.py +++ /dev/null @@ -1,1039 +0,0 @@ -"""Python module which parses and emits TOML. - -Released under the MIT license. -""" -import re -import io -import datetime -from os import linesep -import sys - -__version__ = "0.9.6" -_spec_ = "0.4.0" - - -class TomlDecodeError(Exception): - """Base toml Exception / Error.""" - pass - - -class TomlTz(datetime.tzinfo): - def __init__(self, toml_offset): - if toml_offset == "Z": - self._raw_offset = "+00:00" - else: - self._raw_offset = toml_offset - self._sign = -1 if self._raw_offset[0] == '-' else 1 - self._hours = int(self._raw_offset[1:3]) - self._minutes = int(self._raw_offset[4:6]) - - def tzname(self, dt): - return "UTC" + self._raw_offset - - def utcoffset(self, dt): - return self._sign * datetime.timedelta(hours=self._hours, - minutes=self._minutes) - - def dst(self, dt): - return datetime.timedelta(0) - - -class InlineTableDict(object): - """Sentinel subclass of dict for inline tables.""" - - -def _get_empty_inline_table(_dict): - class DynamicInlineTableDict(_dict, InlineTableDict): - """Concrete sentinel subclass for inline tables. - It is a subclass of _dict which is passed in dynamically at load time - It is also a subclass of InlineTableDict - """ - - return DynamicInlineTableDict() - - -try: - _range = xrange -except NameError: - unicode = str - _range = range - basestring = str - unichr = chr - -try: - FNFError = FileNotFoundError -except NameError: - FNFError = IOError - - -def load(f, _dict=dict): - """Parses named file or files as toml and returns a dictionary - - Args: - f: Path to the file to open, array of files to read into single dict - or a file descriptor - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError -- When f is invalid type - TomlDecodeError: Error while decoding toml - IOError / FileNotFoundError -- When an array with no valid (existing) - (Python 2 / Python 3) file paths is passed - """ - - if isinstance(f, basestring): - with io.open(f, encoding='utf-8') as ffile: - return loads(ffile.read(), _dict) - elif isinstance(f, list): - from os import path as op - from warnings import warn - if not [path for path in f if op.exists(path)]: - error_msg = "Load expects a list to contain filenames only." - error_msg += linesep - error_msg += ("The list needs to contain the path of at least one " - "existing file.") - raise FNFError(error_msg) - d = _dict() - for l in f: - if op.exists(l): - d.update(load(l)) - else: - warn("Non-existent filename in list with at least one valid " - "filename") - return d - else: - try: - return loads(f.read(), _dict) - except AttributeError: - raise TypeError("You can only load a file descriptor, filename or " - "list") - - -_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') - - -def loads(s, _dict=dict): - """Parses string as toml - - Args: - s: String to be parsed - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError: When a non-string is passed - TomlDecodeError: Error while decoding toml - """ - - implicitgroups = [] - retval = _dict() - currentlevel = retval - if not isinstance(s, basestring): - raise TypeError("Expecting something like a string") - - if not isinstance(s, unicode): - s = s.decode('utf8') - - sl = list(s) - openarr = 0 - openstring = False - openstrchar = "" - multilinestr = False - arrayoftables = False - beginline = True - keygroup = False - keyname = 0 - for i, item in enumerate(sl): - if item == '\r' and sl[i + 1] == '\n': - sl[i] = ' ' - continue - if keyname: - if item == '\n': - raise TomlDecodeError("Key name found without value." - " Reached end of line.") - if openstring: - if item == openstrchar: - keyname = 2 - openstring = False - openstrchar = "" - continue - elif keyname == 1: - if item.isspace(): - keyname = 2 - continue - elif item.isalnum() or item == '_' or item == '-': - continue - elif keyname == 2 and item.isspace(): - continue - if item == '=': - keyname = 0 - else: - raise TomlDecodeError("Found invalid character in key name: '" + - item + "'. Try quoting the key name.") - if item == "'" and openstrchar != '"': - k = 1 - try: - while sl[i - k] == "'": - k += 1 - if k == 3: - break - except IndexError: - pass - if k == 3: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = "'" - else: - openstrchar = "" - if item == '"' and openstrchar != "'": - oddbackslash = False - k = 1 - tripquote = False - try: - while sl[i - k] == '"': - k += 1 - if k == 3: - tripquote = True - break - if k == 1 or (k == 3 and tripquote): - while sl[i - k] == '\\': - oddbackslash = not oddbackslash - k += 1 - except IndexError: - pass - if not oddbackslash: - if tripquote: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = '"' - else: - openstrchar = "" - if item == '#' and (not openstring and not keygroup and - not arrayoftables): - j = i - try: - while sl[j] != '\n': - sl[j] = ' ' - j += 1 - except IndexError: - break - if item == '[' and (not openstring and not keygroup and - not arrayoftables): - if beginline: - if len(sl) > i + 1 and sl[i + 1] == '[': - arrayoftables = True - else: - keygroup = True - else: - openarr += 1 - if item == ']' and not openstring: - if keygroup: - keygroup = False - elif arrayoftables: - if sl[i - 1] == ']': - arrayoftables = False - else: - openarr -= 1 - if item == '\n': - if openstring or multilinestr: - if not multilinestr: - raise TomlDecodeError("Unbalanced quotes") - if ((sl[i - 1] == "'" or sl[i - 1] == '"') and ( - sl[i - 2] == sl[i - 1])): - sl[i] = sl[i - 1] - if sl[i - 3] == sl[i - 1]: - sl[i - 3] = ' ' - elif openarr: - sl[i] = ' ' - else: - beginline = True - elif beginline and sl[i] != ' ' and sl[i] != '\t': - beginline = False - if not keygroup and not arrayoftables: - if sl[i] == '=': - raise TomlDecodeError("Found empty keyname. ") - keyname = 1 - s = ''.join(sl) - s = s.split('\n') - multikey = None - multilinestr = "" - multibackslash = False - for line in s: - if not multilinestr or multibackslash or '\n' not in multilinestr: - line = line.strip() - if line == "" and (not multikey or multibackslash): - continue - if multikey: - if multibackslash: - multilinestr += line - else: - multilinestr += line - multibackslash = False - if len(line) > 2 and (line[-1] == multilinestr[0] and - line[-2] == multilinestr[0] and - line[-3] == multilinestr[0]): - try: - value, vtype = _load_value(multilinestr, _dict) - except ValueError as err: - raise TomlDecodeError(str(err)) - currentlevel[multikey] = value - multikey = None - multilinestr = "" - else: - k = len(multilinestr) - 1 - while k > -1 and multilinestr[k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = multilinestr[:-1] - else: - multilinestr += "\n" - continue - if line[0] == '[': - arrayoftables = False - if len(line) == 1: - raise TomlDecodeError("Opening key group bracket on line by " - "itself.") - if line[1] == '[': - arrayoftables = True - line = line[2:] - splitstr = ']]' - else: - line = line[1:] - splitstr = ']' - i = 1 - quotesplits = _get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and splitstr in quotesplit: - break - i += quotesplit.count(splitstr) - quoted = not quoted - line = line.split(splitstr, i) - if len(line) < i + 1 or line[-1].strip() != "": - raise TomlDecodeError("Key group not on a line by itself.") - groups = splitstr.join(line[:-1]).split('.') - i = 0 - while i < len(groups): - groups[i] = groups[i].strip() - if len(groups[i]) > 0 and (groups[i][0] == '"' or - groups[i][0] == "'"): - groupstr = groups[i] - j = i + 1 - while not groupstr[0] == groupstr[-1]: - j += 1 - if j > len(groups) + 2: - raise TomlDecodeError("Invalid group name '" + - groupstr + "' Something " + - "went wrong.") - groupstr = '.'.join(groups[i:j]).strip() - groups[i] = groupstr[1:-1] - groups[i + 1:j] = [] - else: - if not _groupname_re.match(groups[i]): - raise TomlDecodeError("Invalid group name '" + - groups[i] + "'. Try quoting it.") - i += 1 - currentlevel = retval - for i in _range(len(groups)): - group = groups[i] - if group == "": - raise TomlDecodeError("Can't have a keygroup with an empty " - "name") - try: - currentlevel[group] - if i == len(groups) - 1: - if group in implicitgroups: - implicitgroups.remove(group) - if arrayoftables: - raise TomlDecodeError("An implicitly defined " - "table can't be an array") - elif arrayoftables: - currentlevel[group].append(_dict()) - else: - raise TomlDecodeError("What? " + group + - " already exists?" + - str(currentlevel)) - except TypeError: - currentlevel = currentlevel[-1] - try: - currentlevel[group] - except KeyError: - currentlevel[group] = _dict() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [_dict()] - except KeyError: - if i != len(groups) - 1: - implicitgroups.append(group) - currentlevel[group] = _dict() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [_dict()] - currentlevel = currentlevel[group] - if arrayoftables: - try: - currentlevel = currentlevel[-1] - except KeyError: - pass - elif line[0] == "{": - if line[-1] != "}": - raise TomlDecodeError("Line breaks are not allowed in inline" - "objects") - try: - _load_inline_object(line, currentlevel, _dict, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err)) - elif "=" in line: - try: - ret = _load_line(line, currentlevel, _dict, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err)) - if ret is not None: - multikey, multilinestr, multibackslash = ret - return retval - - -def _load_inline_object(line, currentlevel, _dict, multikey=False, - multibackslash=False): - candidate_groups = line[1:-1].split(",") - groups = [] - if len(candidate_groups) == 1 and not candidate_groups[0].strip(): - candidate_groups.pop() - while len(candidate_groups) > 0: - candidate_group = candidate_groups.pop(0) - try: - _, value = candidate_group.split('=', 1) - except ValueError: - raise ValueError("Invalid inline table encountered") - value = value.strip() - if ((value[0] == value[-1] and value[0] in ('"', "'")) or ( - value[0] in '-0123456789' or - value in ('true', 'false') or - (value[0] == "[" and value[-1] == "]") or - (value[0] == '{' and value[-1] == '}'))): - groups.append(candidate_group) - elif len(candidate_groups) > 0: - candidate_groups[0] = candidate_group + "," + candidate_groups[0] - else: - raise ValueError("Invalid inline table value encountered") - for group in groups: - status = _load_line(group, currentlevel, _dict, multikey, - multibackslash) - if status is not None: - break - - -# Matches a TOML number, which allows underscores for readability -_number_with_underscores = re.compile('([0-9])(_([0-9]))*') - - -def _strictly_valid_num(n): - n = n.strip() - if not n: - return False - if n[0] == '_': - return False - if n[-1] == '_': - return False - if "_." in n or "._" in n: - return False - if len(n) == 1: - return True - if n[0] == '0' and n[1] != '.': - return False - if n[0] == '+' or n[0] == '-': - n = n[1:] - if n[0] == '0' and n[1] != '.': - return False - if '__' in n: - return False - return True - - -def _get_split_on_quotes(line): - doublequotesplits = line.split('"') - quoted = False - quotesplits = [] - if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]: - singlequotesplits = doublequotesplits[0].split("'") - doublequotesplits = doublequotesplits[1:] - while len(singlequotesplits) % 2 == 0 and len(doublequotesplits): - singlequotesplits[-1] += '"' + doublequotesplits[0] - doublequotesplits = doublequotesplits[1:] - if "'" in singlequotesplits[-1]: - singlequotesplits = (singlequotesplits[:-1] + - singlequotesplits[-1].split("'")) - quotesplits += singlequotesplits - for doublequotesplit in doublequotesplits: - if quoted: - quotesplits.append(doublequotesplit) - else: - quotesplits += doublequotesplit.split("'") - quoted = not quoted - return quotesplits - - -def _load_line(line, currentlevel, _dict, multikey, multibackslash): - i = 1 - quotesplits = _get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and '=' in quotesplit: - break - i += quotesplit.count('=') - quoted = not quoted - pair = line.split('=', i) - strictly_valid = _strictly_valid_num(pair[-1]) - if _number_with_underscores.match(pair[-1]): - pair[-1] = pair[-1].replace('_', '') - while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and - pair[-1][0] != "'" and pair[-1][0] != '"' and - pair[-1][0] != '[' and pair[-1][0] != '{' and - pair[-1] != 'true' and pair[-1] != 'false'): - try: - float(pair[-1]) - break - except ValueError: - pass - if _load_date(pair[-1]) is not None: - break - i += 1 - prev_val = pair[-1] - pair = line.split('=', i) - if prev_val == pair[-1]: - raise ValueError("Invalid date or number") - if strictly_valid: - strictly_valid = _strictly_valid_num(pair[-1]) - pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()] - if (pair[0][0] == '"' or pair[0][0] == "'") and \ - (pair[0][-1] == '"' or pair[0][-1] == "'"): - pair[0] = pair[0][1:-1] - if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and - pair[1][1] == pair[1][0] and - pair[1][2] == pair[1][0] and - not (len(pair[1]) > 5 and - pair[1][-1] == pair[1][0] and - pair[1][-2] == pair[1][0] and - pair[1][-3] == pair[1][0])): - k = len(pair[1]) - 1 - while k > -1 and pair[1][k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = pair[1][:-1] - else: - multilinestr = pair[1] + "\n" - multikey = pair[0] - else: - value, vtype = _load_value(pair[1], _dict, strictly_valid) - try: - currentlevel[pair[0]] - raise ValueError("Duplicate keys!") - except KeyError: - if multikey: - return multikey, multilinestr, multibackslash - else: - currentlevel[pair[0]] = value - - -def _load_date(val): - microsecond = 0 - tz = None - try: - if len(val) > 19: - if val[19] == '.': - if val[-1].upper() == 'Z': - subsecondval = val[20:-1] - tzval = "Z" - else: - subsecondvalandtz = val[20:] - if '+' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('+') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - elif '-' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('-') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - tz = TomlTz(tzval) - microsecond = int(int(subsecondval) * - (10 ** (6 - len(subsecondval)))) - else: - tz = TomlTz(val[19:]) - except ValueError: - tz = None - if "-" not in val[1:]: - return None - try: - d = datetime.datetime( - int(val[:4]), int(val[5:7]), - int(val[8:10]), int(val[11:13]), - int(val[14:16]), int(val[17:19]), microsecond, tz) - except ValueError: - return None - return d - - -def _load_unicode_escapes(v, hexbytes, prefix): - skip = False - i = len(v) - 1 - while i > -1 and v[i] == '\\': - skip = not skip - i -= 1 - for hx in hexbytes: - if skip: - skip = False - i = len(hx) - 1 - while i > -1 and hx[i] == '\\': - skip = not skip - i -= 1 - v += prefix - v += hx - continue - hxb = "" - i = 0 - hxblen = 4 - if prefix == "\\U": - hxblen = 8 - hxb = ''.join(hx[i:i + hxblen]).lower() - if hxb.strip('0123456789abcdef'): - raise ValueError("Invalid escape sequence: " + hxb) - if hxb[0] == "d" and hxb[1].strip('01234567'): - raise ValueError("Invalid escape sequence: " + hxb + - ". Only scalar unicode points are allowed.") - v += unichr(int(hxb, 16)) - v += unicode(hx[len(hxb):]) - return v - - -# Unescape TOML string values. - -# content after the \ -_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] -# What it should be replaced by -_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] -# Used for substitution -_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) - - -def _unescape(v): - """Unescape characters in a TOML string.""" - i = 0 - backslash = False - while i < len(v): - if backslash: - backslash = False - if v[i] in _escapes: - v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:] - elif v[i] == '\\': - v = v[:i - 1] + v[i:] - elif v[i] == 'u' or v[i] == 'U': - i += 1 - else: - raise ValueError("Reserved escape sequence used") - continue - elif v[i] == '\\': - backslash = True - i += 1 - return v - - -def _load_value(v, _dict, strictly_valid=True): - if not v: - raise ValueError("Empty value is invalid") - if v == 'true': - return (True, "bool") - elif v == 'false': - return (False, "bool") - elif v[0] == '"': - testv = v[1:].split('"') - triplequote = False - triplequotecount = 0 - if len(testv) > 1 and testv[0] == '' and testv[1] == '': - testv = testv[2:] - triplequote = True - closed = False - for tv in testv: - if tv == '': - if triplequote: - triplequotecount += 1 - else: - closed = True - else: - oddbackslash = False - try: - i = -1 - j = tv[i] - while j == '\\': - oddbackslash = not oddbackslash - i -= 1 - j = tv[i] - except IndexError: - pass - if not oddbackslash: - if closed: - raise ValueError("Stuff after closed string. WTF?") - else: - if not triplequote or triplequotecount > 1: - closed = True - else: - triplequotecount = 0 - escapeseqs = v.split('\\')[1:] - backslash = False - for i in escapeseqs: - if i == '': - backslash = not backslash - else: - if i[0] not in _escapes and (i[0] != 'u' and i[0] != 'U' and - not backslash): - raise ValueError("Reserved escape sequence used") - if backslash: - backslash = False - for prefix in ["\\u", "\\U"]: - if prefix in v: - hexbytes = v.split(prefix) - v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], prefix) - v = _unescape(v) - if len(v) > 1 and v[1] == '"' and (len(v) < 3 or v[1] == v[2]): - v = v[2:-2] - return (v[1:-1], "str") - elif v[0] == "'": - if v[1] == "'" and (len(v) < 3 or v[1] == v[2]): - v = v[2:-2] - return (v[1:-1], "str") - elif v[0] == '[': - return (_load_array(v, _dict), "array") - elif v[0] == '{': - inline_object = _get_empty_inline_table(_dict) - _load_inline_object(v, inline_object, _dict) - return (inline_object, "inline_object") - else: - parsed_date = _load_date(v) - if parsed_date is not None: - return (parsed_date, "date") - if not strictly_valid: - raise ValueError("Weirdness with leading zeroes or " - "underscores in your number.") - itype = "int" - neg = False - if v[0] == '-': - neg = True - v = v[1:] - elif v[0] == '+': - v = v[1:] - v = v.replace('_', '') - if '.' in v or 'e' in v or 'E' in v: - if '.' in v and v.split('.', 1)[1] == '': - raise ValueError("This float is missing digits after " - "the point") - if v[0] not in '0123456789': - raise ValueError("This float doesn't have a leading digit") - v = float(v) - itype = "float" - else: - v = int(v) - if neg: - return (0 - v, itype) - return (v, itype) - - -def _bounded_string(s): - if len(s) == 0: - return True - if s[-1] != s[0]: - return False - i = -2 - backslash = False - while len(s) + i > 0: - if s[i] == "\\": - backslash = not backslash - i -= 1 - else: - break - return not backslash - - -def _load_array(a, _dict): - atype = None - retval = [] - a = a.strip() - if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): - strarray = False - tmpa = a[1:-1].strip() - if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): - strarray = True - if not a[1:-1].strip().startswith('{'): - a = a[1:-1].split(',') - else: - # a is an inline object, we must find the matching parenthesis - # to define groups - new_a = [] - start_group_index = 1 - end_group_index = 2 - in_str = False - while end_group_index < len(a[1:]): - if a[end_group_index] == '"' or a[end_group_index] == "'": - if in_str: - backslash_index = end_group_index - 1 - while (backslash_index > -1 and - a[backslash_index] == '\\'): - in_str = not in_str - backslash_index -= 1 - in_str = not in_str - if in_str or a[end_group_index] != '}': - end_group_index += 1 - continue - - # Increase end_group_index by 1 to get the closing bracket - end_group_index += 1 - new_a.append(a[start_group_index:end_group_index]) - - # The next start index is at least after the closing bracket, a - # closing bracket can be followed by a comma since we are in - # an array. - start_group_index = end_group_index + 1 - while (start_group_index < len(a[1:]) and - a[start_group_index] != '{'): - start_group_index += 1 - end_group_index = start_group_index + 1 - a = new_a - b = 0 - if strarray: - while b < len(a) - 1: - ab = a[b].strip() - while (not _bounded_string(ab) or - (len(ab) > 2 and - ab[0] == ab[1] == ab[2] and - ab[-2] != ab[0] and - ab[-3] != ab[0])): - a[b] = a[b] + ',' + a[b + 1] - ab = a[b].strip() - if b < len(a) - 2: - a = a[:b + 1] + a[b + 2:] - else: - a = a[:b + 1] - b += 1 - else: - al = list(a[1:-1]) - a = [] - openarr = 0 - j = 0 - for i in _range(len(al)): - if al[i] == '[': - openarr += 1 - elif al[i] == ']': - openarr -= 1 - elif al[i] == ',' and not openarr: - a.append(''.join(al[j:i])) - j = i + 1 - a.append(''.join(al[j:])) - for i in _range(len(a)): - a[i] = a[i].strip() - if a[i] != '': - nval, ntype = _load_value(a[i], _dict) - if atype: - if ntype != atype: - raise ValueError("Not a homogeneous array") - else: - atype = ntype - retval.append(nval) - return retval - - -def dump(o, f): - """Writes out dict as toml to a file - - Args: - o: Object to dump into toml - f: File descriptor where the toml should be stored - - Returns: - String containing the toml corresponding to dictionary - - Raises: - TypeError: When anything other than file descriptor is passed - """ - - if not f.write: - raise TypeError("You can only dump an object to a file descriptor") - d = dumps(o) - f.write(d) - return d - - -def dumps(o, preserve=False): - """Stringifies input dict as toml - - Args: - o: Object to dump into toml - - preserve: Boolean parameter. If true, preserve inline tables. - - Returns: - String containing the toml corresponding to dict - """ - - retval = "" - addtoretval, sections = _dump_sections(o, "") - retval += addtoretval - while sections != {}: - newsections = {} - for section in sections: - addtoretval, addtosections = _dump_sections(sections[section], - section, preserve) - if addtoretval or (not addtoretval and not addtosections): - if retval and retval[-2:] != "\n\n": - retval += "\n" - retval += "[" + section + "]\n" - if addtoretval: - retval += addtoretval - for s in addtosections: - newsections[section + "." + s] = addtosections[s] - sections = newsections - return retval - - -def _dump_sections(o, sup, preserve=False): - retstr = "" - if sup != "" and sup[-1] != ".": - sup += '.' - retdict = o.__class__() - arraystr = "" - for section in o: - section = unicode(section) - qsection = section - if not re.match(r'^[A-Za-z0-9_-]+$', section): - if '"' in section: - qsection = "'" + section + "'" - else: - qsection = '"' + section + '"' - if not isinstance(o[section], dict): - arrayoftables = False - if isinstance(o[section], list): - for a in o[section]: - if isinstance(a, dict): - arrayoftables = True - if arrayoftables: - for a in o[section]: - arraytabstr = "\n" - arraystr += "[[" + sup + qsection + "]]\n" - s, d = _dump_sections(a, sup + qsection) - if s: - if s[0] == "[": - arraytabstr += s - else: - arraystr += s - while d != {}: - newd = {} - for dsec in d: - s1, d1 = _dump_sections(d[dsec], sup + qsection + - "." + dsec) - if s1: - arraytabstr += ("[" + sup + qsection + "." + - dsec + "]\n") - arraytabstr += s1 - for s1 in d1: - newd[dsec + "." + s1] = d1[s1] - d = newd - arraystr += arraytabstr - else: - if o[section] is not None: - retstr += (qsection + " = " + - unicode(_dump_value(o[section])) + '\n') - elif preserve and isinstance(o[section], InlineTableDict): - retstr += (qsection + " = " + _dump_inline_table(o[section])) - else: - retdict[qsection] = o[section] - retstr += arraystr - return (retstr, retdict) - - -def _dump_inline_table(section): - """Preserve inline table in its compact syntax instead of expanding - into subsection. - - https://github.com/toml-lang/toml#user-content-inline-table - """ - retval = "" - if isinstance(section, dict): - val_list = [] - for k, v in section.items(): - val = _dump_inline_table(v) - val_list.append(k + " = " + val) - retval += "{ " + ", ".join(val_list) + " }\n" - return retval - else: - return unicode(_dump_value(section)) - - -def _dump_value(v): - dump_funcs = { - str: _dump_str, - unicode: _dump_str, - list: _dump_list, - int: lambda v: v, - bool: lambda v: unicode(v).lower(), - float: _dump_float, - datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), - } - # Lookup function corresponding to v's type - dump_fn = dump_funcs.get(type(v)) - if dump_fn is None and hasattr(v, '__iter__'): - dump_fn = dump_funcs[list] - # Evaluate function (if it exists) else return v - return dump_fn(v) if dump_fn is not None else dump_funcs[str](v) - - -def _dump_str(v): - if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): - v = v.decode('utf-8') - v = "%r" % v - if v[0] == 'u': - v = v[1:] - singlequote = v.startswith("'") - if singlequote or v.startswith('"'): - v = v[1:-1] - if singlequote: - v = v.replace("\\'", "'") - v = v.replace('"', '\\"') - v = v.split("\\x") - while len(v) > 1: - i = -1 - if not v[0]: - v = v[1:] - v[0] = v[0].replace("\\\\", "\\") - # No, I don't know why != works and == breaks - joinx = v[0][i] != "\\" - while v[0][:i] and v[0][i] == "\\": - joinx = not joinx - i -= 1 - if joinx: - joiner = "x" - else: - joiner = "u00" - v = [v[0] + joiner + v[1]] + v[2:] - return unicode('"' + v[0] + '"') - - -def _dump_list(v): - retval = "[" - for u in v: - retval += " " + unicode(_dump_value(u)) + "," - retval += "]" - return retval - - -def _dump_float(v): - return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py index 015d73c..7a08fe7 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py @@ -6,16 +6,20 @@ Released under the MIT license. from pip._vendor.toml import encoder from pip._vendor.toml import decoder -__version__ = "0.10.0" +__version__ = "0.10.1" _spec_ = "0.5.0" load = decoder.load loads = decoder.loads TomlDecoder = decoder.TomlDecoder TomlDecodeError = decoder.TomlDecodeError +TomlPreserveCommentDecoder = decoder.TomlPreserveCommentDecoder dump = encoder.dump dumps = encoder.dumps TomlEncoder = encoder.TomlEncoder TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder +TomlNumpyEncoder = encoder.TomlNumpyEncoder +TomlPreserveCommentEncoder = encoder.TomlPreserveCommentEncoder +TomlPathlibEncoder = encoder.TomlPathlibEncoder diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/common.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/common.py new file mode 100644 index 0000000..a5d673d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/common.py @@ -0,0 +1,6 @@ +# content after the \ +escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] +# What it should be replaced by +escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] +# Used for substitution +escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py index 20be459..e488777 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py @@ -24,7 +24,7 @@ def _detect_pathlib_path(p): def _ispath(p): - if isinstance(p, basestring): + if isinstance(p, (bytes, basestring)): return True return _detect_pathlib_path(p) @@ -44,7 +44,7 @@ except NameError: FNFError = IOError -TIME_RE = re.compile("([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]{3,6}))?") +TIME_RE = re.compile(r"([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]{3,6}))?") class TomlDecodeError(ValueError): @@ -66,6 +66,27 @@ class TomlDecodeError(ValueError): _number_with_underscores = re.compile('([0-9])(_([0-9]))*') +class CommentValue(object): + def __init__(self, val, comment, beginline, _dict): + self.val = val + separator = "\n" if beginline else " " + self.comment = separator + comment + self._dict = _dict + + def __getitem__(self, key): + return self.val[key] + + def __setitem__(self, key, value): + self.val[key] = value + + def dump(self, dump_value_func): + retstr = dump_value_func(self.val) + if isinstance(self.val, self._dict): + return self.comment + "\n" + unicode(retstr) + else: + return unicode(retstr) + self.comment + + def _strictly_valid_num(n): n = n.strip() if not n: @@ -96,6 +117,7 @@ def load(f, _dict=dict, decoder=None): f: Path to the file to open, array of files to read into single dict or a file descriptor _dict: (optional) Specifies the class of the returned toml dictionary + decoder: The decoder to use Returns: Parsed toml file represented as a dictionary @@ -120,9 +142,9 @@ def load(f, _dict=dict, decoder=None): "existing file.") raise FNFError(error_msg) if decoder is None: - decoder = TomlDecoder() + decoder = TomlDecoder(_dict) d = decoder.get_empty_table() - for l in f: + for l in f: # noqa: E741 if op.exists(l): d.update(load(l, _dict, decoder)) else: @@ -177,19 +199,30 @@ def loads(s, _dict=dict, decoder=None): keygroup = False dottedkey = False keyname = 0 + key = '' + prev_key = '' + line_no = 1 + for i, item in enumerate(sl): if item == '\r' and sl[i + 1] == '\n': sl[i] = ' ' continue if keyname: + key += item if item == '\n': raise TomlDecodeError("Key name found without value." " Reached end of line.", original, i) if openstring: if item == openstrchar: - keyname = 2 - openstring = False - openstrchar = "" + oddbackslash = False + k = 1 + while i >= k and sl[i - k] == '\\': + oddbackslash = not oddbackslash + k += 1 + if not oddbackslash: + keyname = 2 + openstring = False + openstrchar = "" continue elif keyname == 1: if item.isspace(): @@ -220,6 +253,8 @@ def loads(s, _dict=dict, decoder=None): continue if item == '=': keyname = 0 + prev_key = key[:-1].rstrip() + key = '' dottedkey = False else: raise TomlDecodeError("Found invalid character in key name: '" + @@ -272,12 +307,16 @@ def loads(s, _dict=dict, decoder=None): if item == '#' and (not openstring and not keygroup and not arrayoftables): j = i + comment = "" try: while sl[j] != '\n': + comment += s[j] sl[j] = ' ' j += 1 except IndexError: break + if not openarr: + decoder.preserve_comment(line_no, prev_key, comment, beginline) if item == '[' and (not openstring and not keygroup and not arrayoftables): if beginline: @@ -308,12 +347,20 @@ def loads(s, _dict=dict, decoder=None): sl[i] = ' ' else: beginline = True + line_no += 1 elif beginline and sl[i] != ' ' and sl[i] != '\t': beginline = False if not keygroup and not arrayoftables: if sl[i] == '=': raise TomlDecodeError("Found empty keyname. ", original, i) keyname = 1 + key += item + if keyname: + raise TomlDecodeError("Key name found without value." + " Reached end of file.", original, len(s)) + if openstring: # reached EOF and have an unterminated string + raise TomlDecodeError("Unterminated string found." + " Reached end of file.", original, len(s)) s = ''.join(sl) s = s.split('\n') multikey = None @@ -323,6 +370,9 @@ def loads(s, _dict=dict, decoder=None): for idx, line in enumerate(s): if idx > 0: pos += len(s[idx - 1]) + 1 + + decoder.embed_comments(idx, currentlevel) + if not multilinestr or multibackslash or '\n' not in multilinestr: line = line.strip() if line == "" and (not multikey or multibackslash): @@ -333,9 +383,14 @@ def loads(s, _dict=dict, decoder=None): else: multilinestr += line multibackslash = False - if len(line) > 2 and (line[-1] == multilinestr[0] and - line[-2] == multilinestr[0] and - line[-3] == multilinestr[0]): + closed = False + if multilinestr[0] == '[': + closed = line[-1] == ']' + elif len(line) > 2: + closed = (line[-1] == multilinestr[0] and + line[-2] == multilinestr[0] and + line[-3] == multilinestr[0]) + if closed: try: value, vtype = decoder.load_value(multilinestr) except ValueError as err: @@ -663,7 +718,8 @@ class TomlDecoder(object): while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and pair[-1][0] != "'" and pair[-1][0] != '"' and pair[-1][0] != '[' and pair[-1][0] != '{' and - pair[-1] != 'true' and pair[-1] != 'false'): + pair[-1].strip() != 'true' and + pair[-1].strip() != 'false'): try: float(pair[-1]) break @@ -671,6 +727,8 @@ class TomlDecoder(object): pass if _load_date(pair[-1]) is not None: break + if TIME_RE.match(pair[-1]): + break i += 1 prev_val = pair[-1] pair = line.split('=', i) @@ -704,16 +762,10 @@ class TomlDecoder(object): pair[0] = levels[-1].strip() elif (pair[0][0] == '"' or pair[0][0] == "'") and \ (pair[0][-1] == pair[0][0]): - pair[0] = pair[0][1:-1] - if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and - pair[1][1] == pair[1][0] and - pair[1][2] == pair[1][0] and - not (len(pair[1]) > 5 and - pair[1][-1] == pair[1][0] and - pair[1][-2] == pair[1][0] and - pair[1][-3] == pair[1][0])): - k = len(pair[1]) - 1 - while k > -1 and pair[1][k] == '\\': + pair[0] = _unescape(pair[0][1:-1]) + k, koffset = self._load_line_multiline_str(pair[1]) + if k > -1: + while k > -1 and pair[1][k + koffset] == '\\': multibackslash = not multibackslash k -= 1 if multibackslash: @@ -734,6 +786,26 @@ class TomlDecoder(object): else: currentlevel[pair[0]] = value + def _load_line_multiline_str(self, p): + poffset = 0 + if len(p) < 3: + return -1, poffset + if p[0] == '[' and (p.strip()[-1] != ']' and + self._load_array_isstrarray(p)): + newp = p[1:].strip().split(',') + while len(newp) > 1 and newp[-1][0] != '"' and newp[-1][0] != "'": + newp = newp[:-2] + [newp[-2] + ',' + newp[-1]] + newp = newp[-1] + poffset = len(p) - len(newp) + p = newp + if p[0] != '"' and p[0] != "'": + return -1, poffset + if p[1] != p[0] or p[2] != p[0]: + return -1, poffset + if len(p) > 5 and p[-1] == p[0] and p[-2] == p[0] and p[-3] == p[0]: + return -1, poffset + return len(p) - 1, poffset + def load_value(self, v, strictly_valid=True): if not v: raise ValueError("Empty value is invalid") @@ -769,7 +841,8 @@ class TomlDecoder(object): pass if not oddbackslash: if closed: - raise ValueError("Stuff after closed string. WTF?") + raise ValueError("Found tokens after a closed " + + "string. Invalid TOML.") else: if not triplequote or triplequotecount > 1: closed = True @@ -857,15 +930,18 @@ class TomlDecoder(object): break return not backslash + def _load_array_isstrarray(self, a): + a = a[1:-1].strip() + if a != '' and (a[0] == '"' or a[0] == "'"): + return True + return False + def load_array(self, a): atype = None retval = [] a = a.strip() if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): - strarray = False - tmpa = a[1:-1].strip() - if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): - strarray = True + strarray = self._load_array_isstrarray(a) if not a[1:-1].strip().startswith('{'): a = a[1:-1].split(',') else: @@ -874,6 +950,7 @@ class TomlDecoder(object): new_a = [] start_group_index = 1 end_group_index = 2 + open_bracket_count = 1 if a[start_group_index] == '{' else 0 in_str = False while end_group_index < len(a[1:]): if a[end_group_index] == '"' or a[end_group_index] == "'": @@ -884,9 +961,15 @@ class TomlDecoder(object): in_str = not in_str backslash_index -= 1 in_str = not in_str + if not in_str and a[end_group_index] == '{': + open_bracket_count += 1 if in_str or a[end_group_index] != '}': end_group_index += 1 continue + elif a[end_group_index] == '}' and open_bracket_count > 1: + open_bracket_count -= 1 + end_group_index += 1 + continue # Increase end_group_index by 1 to get the closing bracket end_group_index += 1 @@ -943,3 +1026,27 @@ class TomlDecoder(object): atype = ntype retval.append(nval) return retval + + def preserve_comment(self, line_no, key, comment, beginline): + pass + + def embed_comments(self, idx, currentlevel): + pass + + +class TomlPreserveCommentDecoder(TomlDecoder): + + def __init__(self, _dict=dict): + self.saved_comments = {} + super(TomlPreserveCommentDecoder, self).__init__(_dict) + + def preserve_comment(self, line_no, key, comment, beginline): + self.saved_comments[line_no] = (key, comment, beginline) + + def embed_comments(self, idx, currentlevel): + if idx not in self.saved_comments: + return + + key, comment, beginline = self.saved_comments[idx] + currentlevel[key] = CommentValue(currentlevel[key], comment, beginline, + self._dict) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py index 53b0bd5..a8b03c7 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py @@ -1,6 +1,7 @@ import datetime import re import sys +from decimal import Decimal from pip._vendor.toml.decoder import InlineTableDict @@ -8,12 +9,13 @@ if sys.version_info >= (3,): unicode = str -def dump(o, f): +def dump(o, f, encoder=None): """Writes out dict as toml to a file Args: o: Object to dump into toml f: File descriptor where the toml should be stored + encoder: The ``TomlEncoder`` to use for constructing the output string Returns: String containing the toml corresponding to dictionary @@ -24,7 +26,7 @@ def dump(o, f): if not f.write: raise TypeError("You can only dump an object to a file descriptor") - d = dumps(o) + d = dumps(o, encoder=encoder) f.write(d) return d @@ -34,11 +36,22 @@ def dumps(o, encoder=None): Args: o: Object to dump into toml - - preserve: Boolean parameter. If true, preserve inline tables. + encoder: The ``TomlEncoder`` to use for constructing the output string Returns: String containing the toml corresponding to dict + + Examples: + ```python + >>> import toml + >>> output = { + ... 'a': "I'm a string", + ... 'b': ["I'm", "a", "list"], + ... 'c': 2400 + ... } + >>> toml.dumps(output) + 'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n' + ``` """ retval = "" @@ -46,7 +59,13 @@ def dumps(o, encoder=None): encoder = TomlEncoder(o.__class__) addtoretval, sections = encoder.dump_sections(o, "") retval += addtoretval + outer_objs = [id(o)] while sections: + section_ids = [id(section) for section in sections] + for outer_obj in outer_objs: + if outer_obj in section_ids: + raise ValueError("Circular reference detected") + outer_objs += section_ids newsections = encoder.get_empty_table() for section in sections: addtoretval, addtosections = encoder.dump_sections( @@ -96,7 +115,7 @@ def _dump_str(v): def _dump_float(v): - return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") + return "{}".format(v).replace("e+0", "e+").replace("e-0", "e-") def _dump_time(v): @@ -119,6 +138,7 @@ class TomlEncoder(object): bool: lambda v: unicode(v).lower(), int: lambda v: v, float: _dump_float, + Decimal: _dump_float, datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), datetime.time: _dump_time, datetime.date: lambda v: v.isoformat() @@ -169,10 +189,7 @@ class TomlEncoder(object): section = unicode(section) qsection = section if not re.match(r'^[A-Za-z0-9_-]+$', section): - if '"' in section: - qsection = "'" + section + "'" - else: - qsection = '"' + section + '"' + qsection = _dump_str(section) if not isinstance(o[section], dict): arrayoftables = False if isinstance(o[section], list): @@ -248,3 +265,40 @@ class TomlArraySeparatorEncoder(TomlEncoder): t = s retval += "]" return retval + + +class TomlNumpyEncoder(TomlEncoder): + + def __init__(self, _dict=dict, preserve=False): + import numpy as np + super(TomlNumpyEncoder, self).__init__(_dict, preserve) + self.dump_funcs[np.float16] = _dump_float + self.dump_funcs[np.float32] = _dump_float + self.dump_funcs[np.float64] = _dump_float + self.dump_funcs[np.int16] = self._dump_int + self.dump_funcs[np.int32] = self._dump_int + self.dump_funcs[np.int64] = self._dump_int + + def _dump_int(self, v): + return "{}".format(int(v)) + + +class TomlPreserveCommentEncoder(TomlEncoder): + + def __init__(self, _dict=dict, preserve=False): + from pip._vendor.toml.decoder import CommentValue + super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve) + self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value) + + +class TomlPathlibEncoder(TomlEncoder): + + def _dump_pathlib_path(self, v): + return _dump_str(str(v)) + + def dump_value(self, v): + if (3, 4) <= sys.version_info: + import pathlib + if isinstance(v, pathlib.PurePath): + v = str(v) + return super(TomlPathlibEncoder, self).dump_value(v) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__init__.py index 9bd8323..667e9bc 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__init__.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__init__.py @@ -22,7 +22,7 @@ from logging import NullHandler __author__ = "Andrey Petrov (andrey.petrov@shazow.net)" __license__ = "MIT" -__version__ = "1.25.8" +__version__ = "1.25.9" __all__ = ( "HTTPConnectionPool", diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py index 71e6790..6da1cf4 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +import re import datetime import logging import os @@ -58,6 +59,8 @@ port_by_scheme = {"http": 80, "https": 443} # (ie test_recent_date is failing) update it to ~6 months before the current date. RECENT_DATE = datetime.date(2019, 1, 1) +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + class DummyConnection(object): """Used to detect a failed ConnectionCls import.""" @@ -184,6 +187,17 @@ class HTTPConnection(_HTTPConnection, object): conn = self._new_conn() self._prepare_conn(conn) + def putrequest(self, method, url, *args, **kwargs): + """Send a request to the server""" + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + "Method cannot contain non-token characters %r (found at least %r)" + % (method, match.group()) + ) + + return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) + def request_chunked(self, method, url, body=None, headers=None): """ Alternative to the common request method, which sends the @@ -223,7 +237,12 @@ class HTTPConnection(_HTTPConnection, object): class HTTPSConnection(HTTPConnection): default_port = port_by_scheme["https"] + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ca_cert_data = None ssl_version = None + assert_fingerprint = None def __init__( self, @@ -251,19 +270,6 @@ class HTTPSConnection(HTTPConnection): # HTTPS requests to go out as HTTP. (See Issue #356) self._protocol = "https" - -class VerifiedHTTPSConnection(HTTPSConnection): - """ - Based on httplib.HTTPSConnection but wraps the socket with - SSL certification. - """ - - cert_reqs = None - ca_certs = None - ca_cert_dir = None - ssl_version = None - assert_fingerprint = None - def set_cert( self, key_file=None, @@ -274,6 +280,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): assert_hostname=None, assert_fingerprint=None, ca_cert_dir=None, + ca_cert_data=None, ): """ This method should only be called once, before the connection is used. @@ -294,6 +301,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): self.assert_fingerprint = assert_fingerprint self.ca_certs = ca_certs and os.path.expanduser(ca_certs) self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data def connect(self): # Add certificate verification @@ -344,6 +352,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): if ( not self.ca_certs and not self.ca_cert_dir + and not self.ca_cert_data and default_ssl_context and hasattr(context, "load_default_certs") ): @@ -356,6 +365,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): key_password=self.key_password, ca_certs=self.ca_certs, ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, server_hostname=server_hostname, ssl_context=context, ) @@ -406,9 +416,8 @@ def _match_hostname(cert, asserted_hostname): raise -if ssl: - # Make a copy for testing. - UnverifiedHTTPSConnection = HTTPSConnection - HTTPSConnection = VerifiedHTTPSConnection -else: - HTTPSConnection = DummyConnection +if not ssl: + HTTPSConnection = DummyConnection # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connectionpool.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connectionpool.py index d42eb7b..5f044db 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connectionpool.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -65,6 +65,11 @@ class ConnectionPool(object): """ Base class for all connection pools, such as :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. """ scheme = None @@ -760,21 +765,6 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): **response_kw ) - def drain_and_release_conn(response): - try: - # discard any remaining response body, the connection will be - # released back to the pool once the entire response is read - response.read() - except ( - TimeoutError, - HTTPException, - SocketError, - ProtocolError, - BaseSSLError, - SSLError, - ): - pass - # Handle redirect? redirect_location = redirect and response.get_redirect_location() if redirect_location: @@ -785,15 +775,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_redirect: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep_for_retry(response) log.debug("Redirecting %s -> %s", url, redirect_location) return self.urlopen( @@ -819,15 +805,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_status: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep(response) log.debug("Retry: %s", url) return self.urlopen( diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py index fc99d34..d8fe062 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py @@ -450,9 +450,12 @@ class PyOpenSSLContext(object): cafile = cafile.encode("utf-8") if capath is not None: capath = capath.encode("utf-8") - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("unable to load trusted certificates: %r" % e) def load_cert_chain(self, certfile, keyfile=None, password=None): self._ctx.use_certificate_chain_file(certfile) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/securetransport.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/securetransport.py index 87d844a..a6b7e94 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @@ -819,6 +819,11 @@ class SecureTransportContext(object): if capath is not None: raise ValueError("SecureTransport does not support cert directories") + # Raise if cafile does not exist. + if cafile is not None: + with open(cafile): + pass + self._trust_bundle = cafile or cadata def load_cert_chain(self, certfile, keyfile=None, password=None): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/exceptions.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/exceptions.py index 0a74c79..5cc4d8a 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/exceptions.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/exceptions.py @@ -45,7 +45,10 @@ class SSLError(HTTPError): class ProxyError(HTTPError): "Raised when the connection to a proxy fails." - pass + + def __init__(self, message, error, *args): + super(ProxyError, self).__init__(message, error, *args) + self.original_error = error class DecodeError(HTTPError): @@ -195,6 +198,20 @@ class DependencyWarning(HTTPWarning): pass +class InvalidProxyConfigurationWarning(HTTPWarning): + """ + Warned when using an HTTPS proxy and an HTTPS URL. Currently + urllib3 doesn't support HTTPS proxies and the proxy will be + contacted via HTTP instead. This warning can be fixed by + changing your HTTPS proxy URL into an HTTP proxy URL. + + If you encounter this warning read this: + https://github.com/urllib3/urllib3/issues/1850 + """ + + pass + + class ResponseNotChunked(ProtocolError, ValueError): "Response needs to be chunked in order to read it as chunks." pass diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/poolmanager.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/poolmanager.py index 242a2f8..e2bd3bd 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/poolmanager.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/poolmanager.py @@ -2,11 +2,17 @@ from __future__ import absolute_import import collections import functools import logging +import warnings from ._collections import RecentlyUsedContainer from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool from .connectionpool import port_by_scheme -from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + InvalidProxyConfigurationWarning, +) from .packages import six from .packages.six.moves.urllib.parse import urljoin from .request import RequestMethods @@ -359,6 +365,7 @@ class PoolManager(RequestMethods): retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError: if retries.raise_on_redirect: + response.drain_conn() raise return response @@ -366,6 +373,8 @@ class PoolManager(RequestMethods): kw["redirect"] = redirect log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() return self.urlopen(method, redirect_location, **kw) @@ -452,9 +461,22 @@ class ProxyManager(PoolManager): headers_.update(headers) return headers_ + def _validate_proxy_scheme_url_selection(self, url_scheme): + if url_scheme == "https" and self.proxy.scheme == "https": + warnings.warn( + "Your proxy configuration specified an HTTPS scheme for the proxy. " + "Are you sure you want to use HTTPS to contact the proxy? " + "This most likely indicates an error in your configuration. " + "Read this issue for more info: " + "https://github.com/urllib3/urllib3/issues/1850", + InvalidProxyConfigurationWarning, + stacklevel=3, + ) + def urlopen(self, method, url, redirect=True, **kw): "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." u = parse_url(url) + self._validate_proxy_scheme_url_selection(u.scheme) if u.scheme == "http": # For proxied HTTPS requests, httplib sets the necessary headers diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/response.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/response.py index 6090a73..7dc9b93 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/response.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/response.py @@ -20,6 +20,7 @@ from .exceptions import ( ResponseNotChunked, IncompleteRead, InvalidHeader, + HTTPError, ) from .packages.six import string_types as basestring, PY3 from .packages.six.moves import http_client as httplib @@ -277,6 +278,17 @@ class HTTPResponse(io.IOBase): self._pool._put_conn(self._connection) self._connection = None + def drain_conn(self): + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, SocketError, BaseSSLError, HTTPException): + pass + @property def data(self): # For backwords-compat with earlier urllib3 0.4 and earlier. diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/retry.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/retry.py index 5a049fe..ee30c91 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/retry.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/retry.py @@ -13,6 +13,7 @@ from ..exceptions import ( ReadTimeoutError, ResponseError, InvalidHeader, + ProxyError, ) from ..packages import six @@ -306,6 +307,8 @@ class Retry(object): """ Errors when we're fairly sure that the server did not receive the request, so it should be safe to retry. """ + if isinstance(err, ProxyError): + err = err.original_error return isinstance(err, ConnectTimeoutError) def _is_read_error(self, err): diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/ssl_.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/ssl_.py index 3f78296..d3b463d 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/ssl_.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/ssl_.py @@ -119,12 +119,15 @@ except ImportError: self.certfile = certfile self.keyfile = keyfile - def load_verify_locations(self, cafile=None, capath=None): + def load_verify_locations(self, cafile=None, capath=None, cadata=None): self.ca_certs = cafile if capath is not None: raise SSLError("CA directories not supported in older Pythons") + if cadata is not None: + raise SSLError("CA data not supported in older Pythons") + def set_ciphers(self, cipher_suite): self.ciphers = cipher_suite @@ -305,6 +308,7 @@ def ssl_wrap_socket( ssl_context=None, ca_cert_dir=None, key_password=None, + ca_cert_data=None, ): """ All arguments except for server_hostname, ssl_context, and ca_cert_dir have @@ -323,6 +327,9 @@ def ssl_wrap_socket( SSLContext.load_verify_locations(). :param key_password: Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() """ context = ssl_context if context is None: @@ -331,9 +338,9 @@ def ssl_wrap_socket( # this code. context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) - if ca_certs or ca_cert_dir: + if ca_certs or ca_cert_dir or ca_cert_data: try: - context.load_verify_locations(ca_certs, ca_cert_dir) + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) except IOError as e: # Platform-specific: Python 2.7 raise SSLError(e) # Py33 raises FileNotFoundError which subclasses OSError diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/timeout.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/timeout.py index 9883700..b61fea7 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/timeout.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/timeout.py @@ -98,7 +98,7 @@ class Timeout(object): self.total = self._validate_timeout(total, "total") self._start_connect = None - def __str__(self): + def __repr__(self): return "%s(connect=%r, read=%r, total=%r)" % ( type(self).__name__, self._connect, @@ -106,6 +106,9 @@ class Timeout(object): self.total, ) + # __str__ provided for backwards compatibility + __str__ = __repr__ + @classmethod def _validate_timeout(cls, value, name): """ Check that a timeout attribute is valid. diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/url.py b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/url.py index 5f8aee6..0eb0b6a 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/url.py +++ b/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/url.py @@ -18,7 +18,7 @@ PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") URI_RE = re.compile( r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" - r"(?://([^/?#]*))?" + r"(?://([^\\/?#]*))?" r"([^?#]*)" r"(?:\?([^#]*))?" r"(?:#(.*))?$", diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt b/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt index 74ecca4..06fa135 100644 --- a/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt +++ b/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt @@ -1,24 +1,24 @@ -appdirs==1.4.3 +appdirs==1.4.4 CacheControl==0.12.6 colorama==0.4.3 contextlib2==0.6.0.post1 -distlib==0.3.0 +distlib==0.3.1 distro==1.5.0 -html5lib==1.0.1 +html5lib==1.1 ipaddress==1.0.23 # Only needed on 2.6 and 2.7 msgpack==1.0.0 -packaging==20.3 +packaging==20.4 pep517==0.8.2 progress==1.5 pyparsing==2.4.7 -requests==2.23.0 - certifi==2020.04.05.1 +requests==2.24.0 + certifi==2020.06.20 chardet==3.0.4 - idna==2.9 - urllib3==1.25.8 -resolvelib==0.3.0 + idna==2.10 + urllib3==1.25.9 +resolvelib==0.4.0 retrying==1.3.3 setuptools==44.0.0 -six==1.14.0 -toml==0.10.0 +six==1.15.0 +toml==0.10.1 webencodings==0.5.1 diff --git a/venv/lib/python3.8/site-packages/pyngrok/bin/ngrok b/venv/lib/python3.8/site-packages/pyngrok/bin/ngrok new file mode 100755 index 0000000..a237125 Binary files /dev/null and b/venv/lib/python3.8/site-packages/pyngrok/bin/ngrok differ