From 7498c1491430e1d575e34e38681107463b1695ad Mon Sep 17 00:00:00 2001 From: Rahul Raj Date: Mon, 20 Jan 2020 13:41:59 +0530 Subject: [PATCH] ver0.2.2 --- __pycache__/netizenship.cpython-37.pyc | Bin 0 -> 4844 bytes check_version.py | 28 + dist/netizenship-0.2.0-py3-none-any.whl | Bin 0 -> 4200 bytes dist/netizenship-0.2.0.tar.gz | Bin 0 -> 3665 bytes dist/netizenship-0.2.1-py3-none-any.whl | Bin 0 -> 5045 bytes dist/netizenship-0.2.1.tar.gz | Bin 0 -> 4662 bytes dist/netizenship-0.2.2-py3-none-any.whl | Bin 0 -> 5047 bytes dist/netizenship-0.2.2.tar.gz | Bin 0 -> 4661 bytes netizenship.py | 29 +- pyproject.toml | 5 +- .../__pycache__/rlcompleter.cpython-37.pyc | Bin 0 -> 5802 bytes .../__pycache__/zipp.cpython-37.pyc | Bin 0 -> 6312 bytes .../INSTALLER | 1 + .../LICENSE | 13 + .../METADATA | 65 + .../importlib_metadata-1.4.0.dist-info/RECORD | 33 + .../importlib_metadata-1.4.0.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../importlib_metadata/__init__.py | 589 ++++ .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 20909 bytes .../__pycache__/_compat.cpython-37.pyc | Bin 0 -> 3595 bytes .../importlib_metadata/_compat.py | 131 + .../importlib_metadata/docs/__init__.py | 0 .../docs/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 167 bytes .../docs/__pycache__/conf.cpython-37.pyc | Bin 0 -> 1516 bytes .../importlib_metadata/docs/changelog.rst | 244 ++ .../importlib_metadata/docs/conf.py | 185 ++ .../importlib_metadata/docs/index.rst | 50 + .../importlib_metadata/docs/using.rst | 252 ++ .../importlib_metadata/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 168 bytes .../tests/__pycache__/fixtures.cpython-37.pyc | Bin 0 -> 6115 bytes .../tests/__pycache__/test_api.cpython-37.pyc | Bin 0 -> 7532 bytes .../test_integration.cpython-37.pyc | Bin 0 -> 1193 bytes .../__pycache__/test_main.cpython-37.pyc | Bin 0 -> 9511 bytes .../tests/__pycache__/test_zip.cpython-37.pyc | Bin 0 -> 3049 bytes .../importlib_metadata/tests/data/__init__.py | 0 .../data/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 173 bytes .../tests/data/example-21.12-py3-none-any.whl | Bin 0 -> 1455 bytes .../tests/data/example-21.12-py3.6.egg | Bin 0 -> 1497 bytes .../importlib_metadata/tests/fixtures.py | 200 ++ .../importlib_metadata/tests/test_api.py | 176 ++ .../tests/test_integration.py | 22 + .../importlib_metadata/tests/test_main.py | 224 ++ .../importlib_metadata/tests/test_zip.py | 70 + .../more_itertools-8.1.0.dist-info/INSTALLER | 1 + .../more_itertools-8.1.0.dist-info/LICENSE | 19 + .../more_itertools-8.1.0.dist-info/METADATA | 599 ++++ .../more_itertools-8.1.0.dist-info/RECORD | 16 + .../more_itertools-8.1.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/more_itertools/__init__.py | 4 + .../site-packages/more_itertools/__init__.pyi | 2 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 220 bytes .../__pycache__/more.cpython-37.pyc | Bin 0 -> 83394 bytes .../__pycache__/recipes.cpython-37.pyc | Bin 0 -> 16666 bytes .../site-packages/more_itertools/more.py | 2815 +++++++++++++++++ .../site-packages/more_itertools/more.pyi | 367 +++ .../site-packages/more_itertools/py.typed | 0 .../site-packages/more_itertools/recipes.py | 572 ++++ .../site-packages/more_itertools/recipes.pyi | 100 + .../zipp-2.0.0.dist-info/INSTALLER | 1 + .../zipp-2.0.0.dist-info/LICENSE | 19 + .../zipp-2.0.0.dist-info/METADATA | 51 + .../site-packages/zipp-2.0.0.dist-info/RECORD | 8 + .../site-packages/zipp-2.0.0.dist-info/WHEEL | 5 + .../zipp-2.0.0.dist-info/top_level.txt | 1 + venv/lib/python3.7/site-packages/zipp.py | 220 ++ 68 files changed, 7128 insertions(+), 2 deletions(-) create mode 100644 __pycache__/netizenship.cpython-37.pyc create mode 100644 check_version.py create mode 100644 dist/netizenship-0.2.0-py3-none-any.whl create mode 100644 dist/netizenship-0.2.0.tar.gz create mode 100644 dist/netizenship-0.2.1-py3-none-any.whl create mode 100644 dist/netizenship-0.2.1.tar.gz create mode 100644 dist/netizenship-0.2.2-py3-none-any.whl create mode 100644 dist/netizenship-0.2.2.tar.gz create mode 100644 venv/lib/python3.7/__pycache__/rlcompleter.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/__pycache__/zipp.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/LICENSE create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/METADATA create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/RECORD create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/WHEEL create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/__init__.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/__init__.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/_compat.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/_compat.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/__init__.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/__init__.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/conf.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/changelog.rst create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/conf.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/index.rst create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/docs/using.rst create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__init__.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/__init__.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/fixtures.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_api.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_integration.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_main.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_zip.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/data/__init__.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/data/__pycache__/__init__.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/data/example-21.12-py3-none-any.whl create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/data/example-21.12-py3.6.egg create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/fixtures.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/test_api.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/test_integration.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/test_main.py create mode 100644 venv/lib/python3.7/site-packages/importlib_metadata/tests/test_zip.py create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/LICENSE create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/METADATA create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/RECORD create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/WHEEL create mode 100644 venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.7/site-packages/more_itertools/__init__.py create mode 100644 venv/lib/python3.7/site-packages/more_itertools/__init__.pyi create mode 100644 venv/lib/python3.7/site-packages/more_itertools/__pycache__/__init__.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/more_itertools/__pycache__/more.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/more_itertools/__pycache__/recipes.cpython-37.pyc create mode 100644 venv/lib/python3.7/site-packages/more_itertools/more.py create mode 100644 venv/lib/python3.7/site-packages/more_itertools/more.pyi create mode 100644 venv/lib/python3.7/site-packages/more_itertools/py.typed create mode 100644 venv/lib/python3.7/site-packages/more_itertools/recipes.py create mode 100644 venv/lib/python3.7/site-packages/more_itertools/recipes.pyi create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/LICENSE create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/METADATA create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/RECORD create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/WHEEL create mode 100644 venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.7/site-packages/zipp.py diff --git a/__pycache__/netizenship.cpython-37.pyc b/__pycache__/netizenship.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26fddfa2841dee3da176b8067825a1ff46d100ba GIT binary patch literal 4844 zcmb_g&2Qt@5$7W*k)mwJ>vi@s*)Yj&vP#yH*Iuu^-lUCpoBe2P!&o`phKC3g-?K!E zBALg>^_r2;0#32^AwbYufgbEjf!_NM^wvM1C!(kJv_R2Ak2!UQlq@?4lA@>tKJk6c zo0&KBn;G33A2%fU{CV@*=P%Do(m${>{K)X~5j;^78Y)>5B~Dca2JvTl>{sO^Skuw~k2%YDXtGmaSd?JhH1-wcB= z_H1tMux8{67FA8dFdq9+=yFEQ=I*lD79xz6Yqgdu+Hte$1l<~t_FO-H?z98fVKt=3 znG^W6rWZ78T{{wtk4CFe+c4H1Z0z9hQ0pVeBi1I# z$m@w}UW?3%oEG7R;quc$$X#Dl3aKjCb79F&J1u(5Z;7^5jzZgEw1E?8v1$80IYr`UK9PKnsZ{6*GE*6$_cZ* zVg8WWCW}KqVSaVT?YJQdQVn>khP~RV8;M4+)p)eIxzUJ(E#jzwOh?J&$EdMmr(8B) zz5*^0w?)^hhBgNeOAPe08M_4LTv1z?U#!mGHm`s3Xmf4#hUvK-Wka`N0sP-HABEVipj-Jrce&IZfo|pQnulE$} z_0`=+`vk!-qCz?ZI|OGe_!C0fhWCAij(sin3;XJ}+$(G=bo^`5FZPOuDxEkY+XXnI za9BJ72z@H~(*%COAqYb1XVN#sU;3paG}x!pSN7z-2Iow61$$r3_Ab+N-;n4MJ>SzhMWFT0kL>&t?7Wce z%39E?7Zk5}7&NfR2#%$sySX=iU;$mYINk@n=BJzKeo1Xzj_!Kl&p~V^VN3wI& zLs}y73)oy7PUS+6=ln*o1+j>mEY&u5Tmgs&;aY1$=!X0RUS*eBG8Lx~ zFV#0Nm1w%YCAV+ph-fr<(19H0()8DiQ1{KW zyukw+JK|9c=sU3iC~U3=!h8_KKCO(WI%i+RED}+wfYqexmg`f9bR(R%$~e>jC7a+>Wz(h+4|(_e`B-B#i9M}?CeyxAn#}fK_|;$lj-4<$n7y7y)+!f4XzJ2*y46K zpL_k}+-`2p*$f8eT5@VS%*~|@SC%dLC^MQ?Xn=}=KOFB?$W1@7sNE5I6Iu6ZuCVQ<%7r4VuBUt0)3R687{S(AWjuResXR& z1Z;%Oz~%$0qvJdm(v~(DaupJ;+KBfcgwzMj4$|}8?9f~-#~V?jk;C2QfZ~PWNjMSO zg0sNSnLXyA>566y9xAd#Dmj)6Y7x)QYmwv>{S!sXX@$3ffMTIAP{*~CXX7<<@NK*4 zK>>sXPLB4q zag`;w8`|`l8wEM7LENb&S5LC?Jqael)N*nxmk*Fu-38i!p0*ee!wdPTOFulV8kDOG zfCG!%pf)0b*MjU+qt0&RWO;fnM`rEgRY27iUN;UV6V;s|250?pRm9ztz_yu}{ zWk4x7Z~&|Lv&`nL;#y|rmbQY1y~eB(?8(g;)*o9igpk}`tztd1OiSCy4bdvybH%;P z_V`siA7cm9zgFoXSke=;Bx?-$92$;Qz7Kj_$qmHP9}WzHzlEC(h!eb?-M3ZV1f8zr zS8SD@4od}&1*!$5F=cDnWmqaMw6uGm<`xv#Z{xhSiCSgcDzAb@e~ubuo>i!$GGAg| zl-#Vx-LB1{a7T+UzwDcBJ39GC02KXS_G>Ucgng?7w*W9BpwyPR21Gb!^OXsX3H>13 za{VwC9FryfE;iS(!Fa_n1aK@GA@Q<93_uYCzz*Au`1?3o%!~0#DJ#OPF*|Zxw=t}? z^53i&v?fo)%LZ0HmKs8!(>;uQvMTFj8sgg&;dkN0 zRBtqpheo3^k!pc|p0OBG<%9IJ6lX-2m)C0o;7kllr#f~lM9 zx2hrh;gMXu(Dv8`el+M43{wwLS GPW}V)Ph;Ky literal 0 HcmV?d00001 diff --git a/check_version.py b/check_version.py new file mode 100644 index 0000000..c7aab63 --- /dev/null +++ b/check_version.py @@ -0,0 +1,28 @@ +# credits: https://stackoverflow.com/users/2650249/hoefling + +import json +import urllib.request +import sys + +try: + from importlib.metadata import version +except ImportError: + from importlib_metadata import version + +from distutils.version import LooseVersion + + +def latest_version(): + name = sys.argv[1] + installed_version = LooseVersion(version(name)) + + # fetch package metadata from PyPI + pypi_url = f'https://pypi.org/pypi/{name}/json' + response = urllib.request.urlopen(pypi_url).read().decode() + latest_version = max(LooseVersion(s) for s in json.loads(response)['releases'].keys()) + + # print('package:', name, 'installed:', installed_version, 'latest:', latest_version)\ + return installed_version==latest_version + +if not latest_version(): + print('Do you want to update to latest version?') \ No newline at end of file diff --git a/dist/netizenship-0.2.0-py3-none-any.whl b/dist/netizenship-0.2.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..15dedc564d72d062209f39c274925a2219cfbdb9 GIT binary patch literal 4200 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 literal 0 HcmV?d00001 diff --git a/dist/netizenship-0.2.1-py3-none-any.whl b/dist/netizenship-0.2.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..c8c4b2fd16995e1a5805693579baba58fd867229 GIT binary patch literal 5045 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 literal 0 HcmV?d00001 diff --git a/dist/netizenship-0.2.2.tar.gz b/dist/netizenship-0.2.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4774fa57fd1b9dcdbe2b294c746225ed767ffc1f GIT binary patch literal 4661 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"] +readme = "README.md" +homepage = "https://github.com/rahulrajpl/netizenship" +documentation = "https://github.com/rahulrajpl/netizenship/blob/master/README.md" [tool.poetry.dependencies] python = "^3.7" diff --git a/venv/lib/python3.7/__pycache__/rlcompleter.cpython-37.pyc b/venv/lib/python3.7/__pycache__/rlcompleter.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1437389311b40287f48901f2bb053998b0a230c6 GIT binary patch literal 5802 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�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.7 +Requires-Dist: zipp (>=0.5) +Requires-Dist: pathlib2 ; python_version < "3" +Requires-Dist: contextlib2 ; python_version < "3" +Requires-Dist: configparser (>=3.5) ; python_version < "3" +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: rst.linker ; extra == 'docs' +Provides-Extra: testing +Requires-Dist: packaging ; extra == 'testing' +Requires-Dist: importlib-resources ; (python_version < "3.7") and extra == 'testing' + +========================= + ``importlib_metadata`` +========================= + +``importlib_metadata`` is a library to access the metadata for a Python +package. It is intended to be ported to Python 3.8. + + +Usage +===== + +See the `online documentation `_ +for usage details. + +`Finder authors +`_ can +also add support for custom package installers. See the above documentation +for details. + + +Caveats +======= + +This project primarily supports third-party packages installed by PyPA +tools (or other conforming packages). It does not support: + +- Packages in the stdlib. +- Packages installed without metadata. + +Project details +=============== + + * Project home: https://gitlab.com/python-devs/importlib_metadata + * Report bugs at: https://gitlab.com/python-devs/importlib_metadata/issues + * Code hosting: https://gitlab.com/python-devs/importlib_metadata.git + * Documentation: http://importlib_metadata.readthedocs.io/ + + diff --git a/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/RECORD b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/RECORD new file mode 100644 index 0000000..9d884d7 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/RECORD @@ -0,0 +1,33 @@ +importlib_metadata-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +importlib_metadata-1.4.0.dist-info/LICENSE,sha256=wNe6dAchmJ1VvVB8D9oTc-gHHadCuaSBAev36sYEM6U,571 +importlib_metadata-1.4.0.dist-info/METADATA,sha256=ab04Ma9j7mLVkkTX4N0rP8imRjIEXo0V4soOFyxxNaQ,2093 +importlib_metadata-1.4.0.dist-info/RECORD,, +importlib_metadata-1.4.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110 +importlib_metadata-1.4.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19 +importlib_metadata/__init__.py,sha256=B7TAxzEiiISp6EEz8pfPrny1z88CnZ--d-q2WWEjbPI,18025 +importlib_metadata/__pycache__/__init__.cpython-37.pyc,, +importlib_metadata/__pycache__/_compat.cpython-37.pyc,, +importlib_metadata/_compat.py,sha256=1hcKW_koTLS0lFQt0Mwk8t34oUhHaBoyq6sCzL25vJU,3866 +importlib_metadata/docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +importlib_metadata/docs/__pycache__/__init__.cpython-37.pyc,, +importlib_metadata/docs/__pycache__/conf.cpython-37.pyc,, +importlib_metadata/docs/changelog.rst,sha256=Qwtd-g3HVlimUlPKrWgp2mOHvJRjC617sbV_uEQD5bw,7366 +importlib_metadata/docs/conf.py,sha256=m-b6Mju5gFkpSHh-lyJ4iwqf_8t4LjYYFRumtutQSZc,5578 +importlib_metadata/docs/index.rst,sha256=rbXrDkLAKLIDccqME5u9CCMEfMKprqzQOkIOuwOnfz4,1907 +importlib_metadata/docs/using.rst,sha256=tlh7M8y0hIRB0cYIflhVFQtdQSfm-Q4GE1luXCU4lIY,9286 +importlib_metadata/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +importlib_metadata/tests/__pycache__/__init__.cpython-37.pyc,, +importlib_metadata/tests/__pycache__/fixtures.cpython-37.pyc,, +importlib_metadata/tests/__pycache__/test_api.cpython-37.pyc,, +importlib_metadata/tests/__pycache__/test_integration.cpython-37.pyc,, +importlib_metadata/tests/__pycache__/test_main.cpython-37.pyc,, +importlib_metadata/tests/__pycache__/test_zip.cpython-37.pyc,, +importlib_metadata/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +importlib_metadata/tests/data/__pycache__/__init__.cpython-37.pyc,, +importlib_metadata/tests/data/example-21.12-py3-none-any.whl,sha256=I-kYufETid-tDYyR8f1OFJ3t5u_Io23k0cbQxJTUN4I,1455 +importlib_metadata/tests/data/example-21.12-py3.6.egg,sha256=-EeugFAijkdUO9xyQHTZkQwZoFXK0_QxICBj6R5AAJo,1497 +importlib_metadata/tests/fixtures.py,sha256=sshuoJ4ezljeouUddVg-76K1UOStKWBecovZOKOBguk,5004 +importlib_metadata/tests/test_api.py,sha256=YMAGTsRENrtvpw2CSLmRndJMBeT4q_M0GSe-QsnnMZ4,5544 +importlib_metadata/tests/test_integration.py,sha256=kzqav9qAePjz7UR-GNna65xLwXlRcxEDYDwmuOFwpKE,686 +importlib_metadata/tests/test_main.py,sha256=nnKTmcIA14lhynepCfXtiTYWH35hNFuFfIcKBkzShuY,7179 +importlib_metadata/tests/test_zip.py,sha256=qG3IquiTFLSrUtpxEJblqiUtgEcOTfjU2yM35REk0fo,2372 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/WHEEL b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/WHEEL new file mode 100644 index 0000000..8b701e9 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..bbb0754 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata-1.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +importlib_metadata diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/__init__.py b/venv/lib/python3.7/site-packages/importlib_metadata/__init__.py new file mode 100644 index 0000000..a84cc6f --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/__init__.py @@ -0,0 +1,589 @@ +from __future__ import unicode_literals, absolute_import + +import io +import os +import re +import abc +import csv +import sys +import zipp +import operator +import functools +import itertools +import posixpath +import collections + +from ._compat import ( + install, + NullFinder, + ConfigParser, + suppress, + map, + FileNotFoundError, + IsADirectoryError, + NotADirectoryError, + PermissionError, + pathlib, + ModuleNotFoundError, + MetaPathFinder, + email_message_from_string, + PyPy_repr, + ) +from importlib import import_module +from itertools import starmap + + +__metaclass__ = type + + +__all__ = [ + 'Distribution', + 'DistributionFinder', + 'PackageNotFoundError', + 'distribution', + 'distributions', + 'entry_points', + 'files', + 'metadata', + 'requires', + 'version', + ] + + +class PackageNotFoundError(ModuleNotFoundError): + """The package was not found.""" + + +class EntryPoint( + PyPy_repr, + collections.namedtuple('EntryPointBase', 'name value group')): + """An entry point as defined by Python packaging conventions. + + See `the packaging docs on entry points + `_ + for more information. + """ + + pattern = re.compile( + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+))?\s*' + r'(?P\[.*\])?\s*$' + ) + """ + A regular expression describing the syntax for an entry point, + which might look like: + + - module + - package.module + - package.module:attribute + - package.module:object.attribute + - package.module:attr [extra1, extra2] + + Other combinations are possible as well. + + The expression is lenient about whitespace around the ':', + following the attr, and following any extras. + """ + + def load(self): + """Load the entry point from its definition. If only a module + is indicated by the value, return that module. Otherwise, + return the named object. + """ + match = self.pattern.match(self.value) + module = import_module(match.group('module')) + attrs = filter(None, (match.group('attr') or '').split('.')) + return functools.reduce(getattr, attrs, module) + + @property + def extras(self): + match = self.pattern.match(self.value) + return list(re.finditer(r'\w+', match.group('extras') or '')) + + @classmethod + def _from_config(cls, config): + return [ + cls(name, value, group) + for group in config.sections() + for name, value in config.items(group) + ] + + @classmethod + def _from_text(cls, text): + config = ConfigParser(delimiters='=') + # case sensitive: https://stackoverflow.com/q/1611799/812183 + config.optionxform = str + try: + config.read_string(text) + except AttributeError: # pragma: nocover + # Python 2 has no read_string + config.readfp(io.StringIO(text)) + return EntryPoint._from_config(config) + + def __iter__(self): + """ + Supply iter so one may construct dicts of EntryPoints easily. + """ + return iter((self.name, self)) + + def __reduce__(self): + return ( + self.__class__, + (self.name, self.value, self.group), + ) + + +class PackagePath(pathlib.PurePosixPath): + """A reference to a path in a package""" + + def read_text(self, encoding='utf-8'): + with self.locate().open(encoding=encoding) as stream: + return stream.read() + + def read_binary(self): + with self.locate().open('rb') as stream: + return stream.read() + + def locate(self): + """Return a path-like object for this path""" + return self.dist.locate_file(self) + + +class FileHash: + def __init__(self, spec): + self.mode, _, self.value = spec.partition('=') + + def __repr__(self): + return ''.format(self.mode, self.value) + + +class Distribution: + """A Python distribution package.""" + + @abc.abstractmethod + def read_text(self, filename): + """Attempt to load metadata file given by the name. + + :param filename: The name of the file in the distribution info. + :return: The text if found, otherwise None. + """ + + @abc.abstractmethod + def locate_file(self, path): + """ + Given a path to a file in this distribution, return a path + to it. + """ + + @classmethod + def from_name(cls, name): + """Return the Distribution for the given package name. + + :param name: The name of the distribution package to search for. + :return: The Distribution instance (or subclass thereof) for the named + package, if found. + :raises PackageNotFoundError: When the named package's distribution + metadata cannot be found. + """ + for resolver in cls._discover_resolvers(): + dists = resolver(DistributionFinder.Context(name=name)) + dist = next(dists, None) + if dist is not None: + return dist + else: + raise PackageNotFoundError(name) + + @classmethod + def discover(cls, **kwargs): + """Return an iterable of Distribution objects for all packages. + + Pass a ``context`` or pass keyword arguments for constructing + a context. + + :context: A ``DistributionFinder.Context`` object. + :return: Iterable of Distribution objects for all packages. + """ + context = kwargs.pop('context', None) + if context and kwargs: + raise ValueError("cannot accept context and kwargs") + context = context or DistributionFinder.Context(**kwargs) + return itertools.chain.from_iterable( + resolver(context) + for resolver in cls._discover_resolvers() + ) + + @staticmethod + def at(path): + """Return a Distribution for the indicated metadata path + + :param path: a string or path-like object + :return: a concrete Distribution instance for the path + """ + return PathDistribution(pathlib.Path(path)) + + @staticmethod + def _discover_resolvers(): + """Search the meta_path for resolvers.""" + declared = ( + getattr(finder, 'find_distributions', None) + for finder in sys.meta_path + ) + return filter(None, declared) + + @property + def metadata(self): + """Return the parsed metadata for this Distribution. + + The returned object will have keys that name the various bits of + metadata. See PEP 566 for details. + """ + text = ( + self.read_text('METADATA') + or self.read_text('PKG-INFO') + # This last clause is here to support old egg-info files. Its + # effect is to just end up using the PathDistribution's self._path + # (which points to the egg-info file) attribute unchanged. + or self.read_text('') + ) + return email_message_from_string(text) + + @property + def version(self): + """Return the 'Version' metadata for the distribution package.""" + return self.metadata['Version'] + + @property + def entry_points(self): + return EntryPoint._from_text(self.read_text('entry_points.txt')) + + @property + def files(self): + """Files in this distribution. + + :return: List of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ + file_lines = self._read_files_distinfo() or self._read_files_egginfo() + + def make_file(name, hash=None, size_str=None): + result = PackagePath(name) + result.hash = FileHash(hash) if hash else None + result.size = int(size_str) if size_str else None + result.dist = self + return result + + return file_lines and list(starmap(make_file, csv.reader(file_lines))) + + def _read_files_distinfo(self): + """ + Read the lines of RECORD + """ + text = self.read_text('RECORD') + return text and text.splitlines() + + def _read_files_egginfo(self): + """ + SOURCES.txt might contain literal commas, so wrap each line + in quotes. + """ + text = self.read_text('SOURCES.txt') + return text and map('"{}"'.format, text.splitlines()) + + @property + def requires(self): + """Generated requirements specified for this Distribution""" + reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() + return reqs and list(reqs) + + def _read_dist_info_reqs(self): + return self.metadata.get_all('Requires-Dist') + + def _read_egg_info_reqs(self): + source = self.read_text('requires.txt') + return source and self._deps_from_requires_text(source) + + @classmethod + def _deps_from_requires_text(cls, source): + section_pairs = cls._read_sections(source.splitlines()) + sections = { + section: list(map(operator.itemgetter('line'), results)) + for section, results in + itertools.groupby(section_pairs, operator.itemgetter('section')) + } + return cls._convert_egg_info_reqs_to_simple_reqs(sections) + + @staticmethod + def _read_sections(lines): + section = None + for line in filter(None, lines): + section_match = re.match(r'\[(.*)\]$', line) + if section_match: + section = section_match.group(1) + continue + yield locals() + + @staticmethod + def _convert_egg_info_reqs_to_simple_reqs(sections): + """ + Historically, setuptools would solicit and store 'extra' + requirements, including those with environment markers, + in separate sections. More modern tools expect each + dependency to be defined separately, with any relevant + extras and environment markers attached directly to that + requirement. This method converts the former to the + latter. See _test_deps_from_requires_text for an example. + """ + def make_condition(name): + return name and 'extra == "{name}"'.format(name=name) + + def parse_condition(section): + section = section or '' + extra, sep, markers = section.partition(':') + if extra and markers: + markers = '({markers})'.format(markers=markers) + conditions = list(filter(None, [markers, make_condition(extra)])) + return '; ' + ' and '.join(conditions) if conditions else '' + + for section, deps in sections.items(): + for dep in deps: + yield dep + parse_condition(section) + + +class DistributionFinder(MetaPathFinder): + """ + A MetaPathFinder capable of discovering installed distributions. + """ + + class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ + + name = None + """ + Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. + """ + + def __init__(self, **kwargs): + vars(self).update(kwargs) + + @property + def path(self): + """ + The path that a distribution finder should search. + + Typically refers to Python package paths and defaults + to ``sys.path``. + """ + return vars(self).get('path', sys.path) + + @abc.abstractmethod + def find_distributions(self, context=Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the ``context``, + a DistributionFinder.Context instance. + """ + + +class FastPath: + """ + Micro-optimized class for searching a path for + children. + """ + + def __init__(self, root): + self.root = root + + def joinpath(self, child): + return pathlib.Path(self.root, child) + + def children(self): + with suppress(Exception): + return os.listdir(self.root or '') + with suppress(Exception): + return self.zip_children() + return [] + + def zip_children(self): + zip_path = zipp.Path(self.root) + names = zip_path.root.namelist() + self.joinpath = zip_path.joinpath + + return ( + posixpath.split(child)[0] + for child in names + ) + + def is_egg(self, search): + root_n_low = os.path.split(self.root)[1].lower() + + return ( + root_n_low == search.normalized + '.egg' + or root_n_low.startswith(search.prefix) + and root_n_low.endswith('.egg')) + + def search(self, name): + for child in self.children(): + n_low = child.lower() + if (n_low in name.exact_matches + or n_low.startswith(name.prefix) + and n_low.endswith(name.suffixes) + # legacy case: + or self.is_egg(name) and n_low == 'egg-info'): + yield self.joinpath(child) + + +class Prepared: + """ + A prepared search for metadata on a possibly-named package. + """ + normalized = '' + prefix = '' + suffixes = '.dist-info', '.egg-info' + exact_matches = [''][:0] + + def __init__(self, name): + self.name = name + if name is None: + return + self.normalized = name.lower().replace('-', '_') + self.prefix = self.normalized + '-' + self.exact_matches = [ + self.normalized + suffix for suffix in self.suffixes] + + +@install +class MetadataPathFinder(NullFinder, DistributionFinder): + """A degenerate finder for distribution packages on the file system. + + This finder supplies only a find_distributions() method for versions + of Python that do not have a PathFinder find_distributions(). + """ + + def find_distributions(self, context=DistributionFinder.Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching ``context.name`` + (or all names if ``None`` indicated) along the paths in the list + of directories ``context.path``. + """ + found = self._search_paths(context.name, context.path) + return map(PathDistribution, found) + + @classmethod + def _search_paths(cls, name, paths): + """Find metadata directories in paths heuristically.""" + return itertools.chain.from_iterable( + path.search(Prepared(name)) + for path in map(FastPath, paths) + ) + + +class PathDistribution(Distribution): + def __init__(self, path): + """Construct a distribution from a path to the metadata directory. + + :param path: A pathlib.Path or similar object supporting + .joinpath(), __div__, .parent, and .read_text(). + """ + self._path = path + + def read_text(self, filename): + with suppress(FileNotFoundError, IsADirectoryError, KeyError, + NotADirectoryError, PermissionError): + return self._path.joinpath(filename).read_text(encoding='utf-8') + read_text.__doc__ = Distribution.read_text.__doc__ + + def locate_file(self, path): + return self._path.parent / path + + +def distribution(distribution_name): + """Get the ``Distribution`` instance for the named package. + + :param distribution_name: The name of the distribution package as a string. + :return: A ``Distribution`` instance (or subclass thereof). + """ + return Distribution.from_name(distribution_name) + + +def distributions(**kwargs): + """Get all ``Distribution`` instances in the current environment. + + :return: An iterable of ``Distribution`` instances. + """ + return Distribution.discover(**kwargs) + + +def metadata(distribution_name): + """Get the metadata for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: An email.Message containing the parsed metadata. + """ + return Distribution.from_name(distribution_name).metadata + + +def version(distribution_name): + """Get the version string for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: The version string for the package as defined in the package's + "Version" metadata key. + """ + return distribution(distribution_name).version + + +def entry_points(): + """Return EntryPoint objects for all installed packages. + + :return: EntryPoint objects for all installed packages. + """ + eps = itertools.chain.from_iterable( + dist.entry_points for dist in distributions()) + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return { + group: tuple(eps) + for group, eps in grouped + } + + +def files(distribution_name): + """Return a list of files for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: List of files composing the distribution. + """ + return distribution(distribution_name).files + + +def requires(distribution_name): + """ + Return a list of requirements for the named package. + + :return: An iterator of requirements, suitable for + packaging.requirement.Requirement. + """ + return distribution(distribution_name).requires + + +__version__ = version(__name__) diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f9cc74903a431727720ee4682ec4b2daa5761cf GIT binary patch literal 20909 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/_compat.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/__pycache__/_compat.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce3be37ca880c7213db640d938254e4686535862 GIT binary patch literal 3595 zcmZ`*&2k&Z5uVvy01FTVNs!{7X?aE2wBQmUOG(OcmEtn>=aQ=gvdn|*O(mPf&VXES z|1`5;42V2YRq-j6Lmogn<}LCBIpjFkob(E*R3+WBOHia0kUO(GGu_kE{q;BfV0N}a z;P>C>pZtD(j*x%h$N6stUcQD${*@BK2%`~+sY@xo8aL6%uv61rdrp!Ki|}>+ z!N53bi2qQ6e|*DT`++3J_oO^govSqdc{l_4^%9cs!xw?(g~e;gc|7Tm&#W{Q(XNM6}_B z6PGgL8r&ROZiP{>#}GD5^g2~#dtTzl-1Ah!^Wv0^B7ARp-iwhRl{2Q_4^(aE!9(0p z&B8k9gUFZC^ZrHzz_Dol{q3DR&hBJkwjCxi_oHZglzlk}?*=dK6vvIi{x%9TKld+& zv_3uL1!_bKj7X#q_HP+5he!#`WAoK zV>}+hBUhox$&kJ_PRJQ~1G$%*XRzdifVb)7pA83o57A`~<3TEX*vc8eQy$3A!!&W= zUyfxLJ>;SN-G-LiVN5#tA$R(IaD=FF9))O3e}qzn7o0r3EbI6Q)6#yP^Ek^vL+Y?l z`u&J&WdjWxXD~_vRO3fsK6cW9o|8EP-)ZrmZ`IY~9ZQ*Dtg26~RaHOBc*4}RavSg% zs_}}Q0LEqz$qTrMd2pIofaX`|!YVYxZqX{8LK#Cpb@3K_Fv^Tq! zeJiz^{@97rAVOem9>oFR|>6~3}w~W(1=g!S9?`q|7DvEd=2n3}bgQ#mqa}0ET z7)nPTaZorecHxKqahQt2y+4E5J{(^zL32We;D;k4ZRhj^2>uKS#_XweMCpVlC9|O8 z`Hwjl5Lwm65O|kP1{{DlimDy@5+H(%Q!N5@B|@qe;( zEMFW*RXzG6_E%h?>Zsn684tckanwbO$&RuiCO_5937DZV%>U}di~L00EoE^3$>Ds@4b(cIqY8` z(`-<22fp49l-n?K87RDh2?P)^nFTj`jaS7MuetVbtE>X{S5&py$8=ghPI58cOT#30(*6)`5sCSYa!9%UaT+qm^Fqh~&vX1BfIFoC$aj1IeDTF? z=jPqd?-fI8Asc675B88i6<(MO(y7bQ-FSy#FYzdUA_J-!5%`apuYnIw=uZSW5yD_F zYiJHgZk>@oV<;-UsiO1PEuy00=K2X}<;(yzbVxzSD20sPWd0>q0jIxtdF)3cenXsg zOjSF8bOFwQ%uWR)x~djgk0Hd>s-hQT;Ual7D8h)4QjOR@;&8VKKYoe3YdWDoC)-m$ zcfGO-&|devcPZsuc4FLiCQBENzx7h9l&76eMOj0Lg}8>>)!^>o5tHH<@Fji;O~({p z;QOy}x>`tS`#Q9k&}PnEv0(zM3ofEfvrQ*!|JT}Hr+y^|#izKRgAEd(Yx@B0Am>cn zhfgfP5J}Kpfc+u`KZhj}pC&Yzf(^s2S=6EeXF8u@)ixOABLP*Lz_^Ofu));@-6Bp@ z^IbeCyFUUxhQdQN16^{07og-UDy0StVHRTPgkTfPs9@o#yiy^F6&#=2<01|vV)ZeW zUE1sJ^7(ZuyYFtG>X-w0nnDqPjG|^P_PVEPPj|HnRomS!bRny_c3H>W6Npw-?*Y7u zb*&vhAl5+T_smmWQR_)}c3N+Cp?EEd$7+$Cz%t{Eix#Q_dW0$!T!&SNzG3t&p(#Wy zUZSX?`V*Kg+V}4Zm$L5{bov$2z5EWEreRb42h3#CCj3>)Kij~sW-&``xKkmvh5^aY brVX=6TgJR)fi%cj_N=|oYTNaBTDATM+i#^p literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/_compat.py b/venv/lib/python3.7/site-packages/importlib_metadata/_compat.py new file mode 100644 index 0000000..40ccc7e --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/_compat.py @@ -0,0 +1,131 @@ +from __future__ import absolute_import + +import io +import abc +import sys +import email + + +if sys.version_info > (3,): # pragma: nocover + import builtins + from configparser import ConfigParser + from contextlib import suppress + FileNotFoundError = builtins.FileNotFoundError + IsADirectoryError = builtins.IsADirectoryError + NotADirectoryError = builtins.NotADirectoryError + PermissionError = builtins.PermissionError + map = builtins.map +else: # pragma: nocover + from backports.configparser import ConfigParser + from itertools import imap as map # type: ignore + from contextlib2 import suppress # noqa + FileNotFoundError = IOError, OSError + IsADirectoryError = IOError, OSError + NotADirectoryError = IOError, OSError + PermissionError = IOError, OSError + +if sys.version_info > (3, 5): # pragma: nocover + import pathlib +else: # pragma: nocover + import pathlib2 as pathlib + +try: + ModuleNotFoundError = builtins.FileNotFoundError +except (NameError, AttributeError): # pragma: nocover + ModuleNotFoundError = ImportError # type: ignore + + +if sys.version_info >= (3,): # pragma: nocover + from importlib.abc import MetaPathFinder +else: # pragma: nocover + class MetaPathFinder(object): + __metaclass__ = abc.ABCMeta + + +__metaclass__ = type +__all__ = [ + 'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError', + 'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError', + 'NotADirectoryError', 'email_message_from_string', + ] + + +def install(cls): + """ + Class decorator for installation on sys.meta_path. + + Adds the backport DistributionFinder to sys.meta_path and + attempts to disable the finder functionality of the stdlib + DistributionFinder. + """ + sys.meta_path.append(cls()) + disable_stdlib_finder() + return cls + + +def disable_stdlib_finder(): + """ + Give the backport primacy for discovering path-based distributions + by monkey-patching the stdlib O_O. + + See #91 for more background for rationale on this sketchy + behavior. + """ + def matches(finder): + return ( + finder.__module__ == '_frozen_importlib_external' + and hasattr(finder, 'find_distributions') + ) + for finder in filter(matches, sys.meta_path): # pragma: nocover + del finder.find_distributions + + +class NullFinder: + """ + A "Finder" (aka "MetaClassFinder") that never finds any modules, + but may find distributions. + """ + @staticmethod + def find_spec(*args, **kwargs): + return None + + # In Python 2, the import system requires finders + # to have a find_module() method, but this usage + # is deprecated in Python 3 in favor of find_spec(). + # For the purposes of this finder (i.e. being present + # on sys.meta_path but having no other import + # system functionality), the two methods are identical. + find_module = find_spec + + +def py2_message_from_string(text): # nocoverpy3 + # Work around https://bugs.python.org/issue25545 where + # email.message_from_string cannot handle Unicode on Python 2. + io_buffer = io.StringIO(text) + return email.message_from_file(io_buffer) + + +email_message_from_string = ( + py2_message_from_string + if sys.version_info < (3,) else + email.message_from_string + ) + + +class PyPy_repr: + """ + Override repr for EntryPoint objects on PyPy to avoid __iter__ access. + Ref #97, #102. + """ + affected = hasattr(sys, 'pypy_version_info') + + def __compat_repr__(self): # pragma: nocover + def make_param(name): + value = getattr(self, name) + return '{name}={value!r}'.format(**locals()) + params = ', '.join(map(make_param, self._fields)) + return 'EntryPoint({params})'.format(**locals()) + + if affected: # pragma: nocover + __repr__ = __compat_repr__ + del affected diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/__init__.py b/venv/lib/python3.7/site-packages/importlib_metadata/docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d45a5d5ce0a01ee06e4b0134280f445565505532 GIT binary patch literal 167 zcmZ?b<>g`kg8$*FaUl9Jh=2h`Aj1KOi&=m~3PUi1CZpdKjrQ|0U>&M4u=4F<| Y$LkeT-r}&y%}*)KNwovn^cjd50Fodp#Q*>R literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/conf.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/docs/__pycache__/conf.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e24d88b213a28e38642c1dda72f67d05aa7545c8 GIT binary patch literal 1516 zcmaJ>?{eEj5V!nCw$<2ak|w2vg4zsFwz&dP%RbjC%TxH9m!Y*SC-hpdy9o`)`2ziC%HoS)v1#ZkV zt7jV0zYiabn`E^HF5HA$zf{>3xQ*+$4j;lMeDu0XULU`<2!8`Q`;~sLa58PYSje9+ zD^_(H2|4j_VmzK31u7`k&glYmh#3#@f*~0h)TY=tXDFn>Y%X5_I23pkC_v`;29q@A zEMo##GQDjDf5caLY; zvHP57njaN6{{Ogk&)a^+Y=4xC7>f4yQBDvZjEc7RWZ&O6Dnn+^)BbYW1RV1`HbwQ$ zouZo|B`cKFUSyKE=xXFy5RKGiCbRXo(=IP><88HPa8le{=nocUx=&P)Cn(JTuLP`mdWgd%bdrByMXx*ls_#ZXyJY%(WNIw7f=V8NXtT8HFncX{fL3_Sm$e@ z1B_!XQRTW{Ev`kzr24^dNL6SrJvNb&p31`EC(r2g)3eh>{6U6V=d|BHkS_+Gzr1@Oi(4mjz2?+8$?))n5eX5KC+W)vn?DRTlffpOIu2#g zOt~R~O4d3OCh`frZ%vyS5+MWZU!6LHfwGLIS$9EHpS&X0nb+y-d^{Et<|JGbluzXn z?EF5b?Lei+nFu2@wW*IHl}(#((l5ir$Tdz|blYK0-9#-u3E~{E3{a*;LDS=~q=NRf zIgXL7mc8f;IX5c>&76{mKHYSJtTWqd0XU*YU@N5-c zrD~lGyKdX{8V!p^vtFgya7wvVZ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/changelog.rst b/venv/lib/python3.7/site-packages/importlib_metadata/docs/changelog.rst new file mode 100644 index 0000000..8436304 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/docs/changelog.rst @@ -0,0 +1,244 @@ +========================= + importlib_metadata NEWS +========================= + +v1.4.0 +====== + +* Through careful optimization, ``distribution()`` is + 3-4x faster. Thanks to Antony Lee for the + contribution. Closes #95. + +v1.3.0 +====== + +* Improve custom finders documentation. Closes #105. + +v1.2.0 +====== + +* Once again, drop support for Python 3.4. Ref #104. + +v1.1.3 +====== + +* Restored support for Python 3.4 due to improper version + compatibility declarations in the v1.1.0 and v1.1.1 + releases. Closes #104. + +v1.1.2 +====== + +* Repaired project metadata to correctly declare the + ``python_requires`` directive. Closes #103. + +v1.1.1 +====== + +* Fixed ``repr(EntryPoint)`` on PyPy 3 also. Closes #102. + +v1.1.0 +====== + +* Dropped support for Python 3.4. +* EntryPoints are now pickleable. Closes #96. +* Fixed ``repr(EntryPoint)`` on PyPy 2. Closes #97. + +v1.0.0 +====== + +* Project adopts semver for versioning. + +* Removed compatibility shim introduced in 0.23. + +* For better compatibility with the stdlib implementation and to + avoid the same distributions being discovered by the stdlib and + backport implementations, the backport now disables the + stdlib DistributionFinder during initialization (import time). + Closes #91 and closes #100. + +0.23 +==== +* Added a compatibility shim to prevent failures on beta releases + of Python before the signature changed to accept the + "context" parameter on find_distributions. This workaround + will have a limited lifespan, not to extend beyond release of + Python 3.8 final. + +0.22 +==== +* Renamed ``package`` parameter to ``distribution_name`` + as `recommended `_ + in the following functions: ``distribution``, ``metadata``, + ``version``, ``files``, and ``requires``. This + backward-incompatible change is expected to have little impact + as these functions are assumed to be primarily used with + positional parameters. + +0.21 +==== +* ``importlib.metadata`` now exposes the ``DistributionFinder`` + metaclass and references it in the docs for extending the + search algorithm. +* Add ``Distribution.at`` for constructing a Distribution object + from a known metadata directory on the file system. Closes #80. +* Distribution finders now receive a context object that + supplies ``.path`` and ``.name`` properties. This change + introduces a fundamental backward incompatibility for + any projects implementing a ``find_distributions`` method + on a ``MetaPathFinder``. This new layer of abstraction + allows this context to be supplied directly or constructed + on demand and opens the opportunity for a + ``find_distributions`` method to solicit additional + context from the caller. Closes #85. + +0.20 +==== +* Clarify in the docs that calls to ``.files`` could return + ``None`` when the metadata is not present. Closes #69. +* Return all requirements and not just the first for dist-info + packages. Closes #67. + +0.19 +==== +* Restrain over-eager egg metadata resolution. +* Add support for entry points with colons in the name. Closes #75. + +0.18 +==== +* Parse entry points case sensitively. Closes #68 +* Add a version constraint on the backport configparser package. Closes #66 + +0.17 +==== +* Fix a permission problem in the tests on Windows. + +0.16 +==== +* Don't crash if there exists an EGG-INFO directory on sys.path. + +0.15 +==== +* Fix documentation. + +0.14 +==== +* Removed ``local_distribution`` function from the API. + **This backward-incompatible change removes this + behavior summarily**. Projects should remove their + reliance on this behavior. A replacement behavior is + under review in the `pep517 project + `_. Closes #42. + +0.13 +==== +* Update docstrings to match PEP 8. Closes #63. +* Merged modules into one module. Closes #62. + +0.12 +==== +* Add support for eggs. !65; Closes #19. + +0.11 +==== +* Support generic zip files (not just wheels). Closes #59 +* Support zip files with multiple distributions in them. Closes #60 +* Fully expose the public API in ``importlib_metadata.__all__``. + +0.10 +==== +* The ``Distribution`` ABC is now officially part of the public API. + Closes #37. +* Fixed support for older single file egg-info formats. Closes #43. +* Fixed a testing bug when ``$CWD`` has spaces in the path. Closes #50. +* Add Python 3.8 to the ``tox`` testing matrix. + +0.9 +=== +* Fixed issue where entry points without an attribute would raise an + Exception. Closes #40. +* Removed unused ``name`` parameter from ``entry_points()``. Closes #44. +* ``DistributionFinder`` classes must now be instantiated before + being placed on ``sys.meta_path``. + +0.8 +=== +* This library can now discover/enumerate all installed packages. **This + backward-incompatible change alters the protocol finders must + implement to support distribution package discovery.** Closes #24. +* The signature of ``find_distributions()`` on custom installer finders + should now accept two parameters, ``name`` and ``path`` and + these parameters must supply defaults. +* The ``entry_points()`` method no longer accepts a package name + but instead returns all entry points in a dictionary keyed by the + ``EntryPoint.group``. The ``resolve`` method has been removed. Instead, + call ``EntryPoint.load()``, which has the same semantics as + ``pkg_resources`` and ``entrypoints``. **This is a backward incompatible + change.** +* Metadata is now always returned as Unicode text regardless of + Python version. Closes #29. +* This library can now discover metadata for a 'local' package (found + in the current-working directory). Closes #27. +* Added ``files()`` function for resolving files from a distribution. +* Added a new ``requires()`` function, which returns the requirements + for a package suitable for parsing by + ``packaging.requirements.Requirement``. Closes #18. +* The top-level ``read_text()`` function has been removed. Use + ``PackagePath.read_text()`` on instances returned by the ``files()`` + function. **This is a backward incompatible change.** +* Release dates are now automatically injected into the changelog + based on SCM tags. + +0.7 +=== +* Fixed issue where packages with dashes in their names would + not be discovered. Closes #21. +* Distribution lookup is now case-insensitive. Closes #20. +* Wheel distributions can no longer be discovered by their module + name. Like Path distributions, they must be indicated by their + distribution package name. + +0.6 +=== +* Removed ``importlib_metadata.distribution`` function. Now + the public interface is primarily the utility functions exposed + in ``importlib_metadata.__all__``. Closes #14. +* Added two new utility functions ``read_text`` and + ``metadata``. + +0.5 +=== +* Updated README and removed details about Distribution + class, now considered private. Closes #15. +* Added test suite support for Python 3.4+. +* Fixed SyntaxErrors on Python 3.4 and 3.5. !12 +* Fixed errors on Windows joining Path elements. !15 + +0.4 +=== +* Housekeeping. + +0.3 +=== +* Added usage documentation. Closes #8 +* Add support for getting metadata from wheels on ``sys.path``. Closes #9 + +0.2 +=== +* Added ``importlib_metadata.entry_points()``. Closes #1 +* Added ``importlib_metadata.resolve()``. Closes #12 +* Add support for Python 2.7. Closes #4 + +0.1 +=== +* Initial release. + + +.. + Local Variables: + mode: change-log-mode + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 78 + coding: utf-8 + End: diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/conf.py b/venv/lib/python3.7/site-packages/importlib_metadata/docs/conf.py new file mode 100644 index 0000000..129a7a4 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/docs/conf.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# importlib_metadata documentation build configuration file, created by +# sphinx-quickstart on Thu Nov 30 10:21:00 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'rst.linker', + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', + ] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'importlib_metadata' +copyright = '2017-2019, Jason R. Coombs, Barry Warsaw' +author = 'Jason R. Coombs, Barry Warsaw' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'default' + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + ] + } + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'importlib_metadatadoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', + } + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'importlib_metadata.tex', + 'importlib\\_metadata Documentation', + 'Brett Cannon, Barry Warsaw', 'manual'), + ] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'importlib_metadata', 'importlib_metadata Documentation', + [author], 1) + ] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'importlib_metadata', 'importlib_metadata Documentation', + author, 'importlib_metadata', 'One line description of project.', + 'Miscellaneous'), + ] + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'importlib_resources': ( + 'https://importlib-resources.readthedocs.io/en/latest/', None + ), + } + + +# For rst.linker, inject release dates into changelog.rst +link_files = { + 'changelog.rst': dict( + replace=[ + dict( + pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', + with_scm='{text}\n{rev[timestamp]:%Y-%m-%d}\n\n', + ), + ], + ), + } diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/index.rst b/venv/lib/python3.7/site-packages/importlib_metadata/docs/index.rst new file mode 100644 index 0000000..530197c --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/docs/index.rst @@ -0,0 +1,50 @@ +=============================== + Welcome to importlib_metadata +=============================== + +``importlib_metadata`` is a library which provides an API for accessing an +installed package's metadata (see :pep:`566`), such as its entry points or its top-level +name. This functionality intends to replace most uses of ``pkg_resources`` +`entry point API`_ and `metadata API`_. Along with :mod:`importlib.resources` in +Python 3.7 and newer (backported as :doc:`importlib_resources ` for older +versions of Python), this can eliminate the need to use the older and less +efficient ``pkg_resources`` package. + +``importlib_metadata`` is a backport of Python 3.8's standard library +:doc:`importlib.metadata ` module for Python 2.7, and 3.4 through 3.7. Users of +Python 3.8 and beyond are encouraged to use the standard library module. +When imported on Python 3.8 and later, ``importlib_metadata`` replaces the +DistributionFinder behavior from the stdlib, but leaves the API in tact. +Developers looking for detailed API descriptions should refer to the Python +3.8 standard library documentation. + +The documentation here includes a general :ref:`usage ` guide. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + using.rst + changelog (links).rst + + +Project details +=============== + + * Project home: https://gitlab.com/python-devs/importlib_metadata + * Report bugs at: https://gitlab.com/python-devs/importlib_metadata/issues + * Code hosting: https://gitlab.com/python-devs/importlib_metadata.git + * Documentation: http://importlib_metadata.readthedocs.io/ + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + +.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points +.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/docs/using.rst b/venv/lib/python3.7/site-packages/importlib_metadata/docs/using.rst new file mode 100644 index 0000000..6da2bb1 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/docs/using.rst @@ -0,0 +1,252 @@ +.. _using: + +================================= + Using :mod:`!importlib_metadata` +================================= + +``importlib_metadata`` is a library that provides for access to installed +package metadata. Built in part on Python's import system, this library +intends to replace similar functionality in the `entry point +API`_ and `metadata API`_ of ``pkg_resources``. Along with +:mod:`importlib.resources` in Python 3.7 +and newer (backported as :doc:`importlib_resources ` for older versions of +Python), this can eliminate the need to use the older and less efficient +``pkg_resources`` package. + +By "installed package" we generally mean a third-party package installed into +Python's ``site-packages`` directory via tools such as `pip +`_. Specifically, +it means a package with either a discoverable ``dist-info`` or ``egg-info`` +directory, and metadata defined by :pep:`566` or its older specifications. +By default, package metadata can live on the file system or in zip archives on +:data:`sys.path`. Through an extension mechanism, the metadata can live almost +anywhere. + + +Overview +======== + +Let's say you wanted to get the version string for a package you've installed +using ``pip``. We start by creating a virtual environment and installing +something into it:: + + $ python3 -m venv example + $ source example/bin/activate + (example) $ pip install importlib_metadata + (example) $ pip install wheel + +You can get the version string for ``wheel`` by running the following:: + + (example) $ python + >>> from importlib_metadata import version + >>> version('wheel') + '0.32.3' + +You can also get the set of entry points keyed by group, such as +``console_scripts``, ``distutils.commands`` and others. Each group contains a +sequence of :ref:`EntryPoint ` objects. + +You can get the :ref:`metadata for a distribution `:: + + >>> list(metadata('wheel')) + ['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist'] + +You can also get a :ref:`distribution's version number `, list its +:ref:`constituent files `, and get a list of the distribution's +:ref:`requirements`. + + +Functional API +============== + +This package provides the following functionality via its public API. + + +.. _entry-points: + +Entry points +------------ + +The ``entry_points()`` function returns a dictionary of all entry points, +keyed by group. Entry points are represented by ``EntryPoint`` instances; +each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and +a ``.load()`` method to resolve the value:: + + >>> eps = entry_points() + >>> list(eps) + ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] + >>> scripts = eps['console_scripts'] + >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] + >>> wheel + EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') + >>> main = wheel.load() + >>> main + + +The ``group`` and ``name`` are arbitrary values defined by the package author +and usually a client will wish to resolve all entry points for a particular +group. Read `the setuptools docs +`_ +for more information on entrypoints, their definition, and usage. + + +.. _metadata: + +Distribution metadata +--------------------- + +Every distribution includes some metadata, which you can extract using the +``metadata()`` function:: + + >>> wheel_metadata = metadata('wheel') + +The keys of the returned data structure [#f1]_ name the metadata keywords, and +their values are returned unparsed from the distribution metadata:: + + >>> wheel_metadata['Requires-Python'] + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + + +.. _version: + +Distribution versions +--------------------- + +The ``version()`` function is the quickest way to get a distribution's version +number, as a string:: + + >>> version('wheel') + '0.32.3' + + +.. _files: + +Distribution files +------------------ + +You can also get the full set of files contained within a distribution. The +``files()`` function takes a distribution package name and returns all of the +files installed by this distribution. Each file object returned is a +``PackagePath``, a :class:`pathlib.Path` derived object with additional ``dist``, +``size``, and ``hash`` properties as indicated by the metadata. For example:: + + >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] + >>> util + PackagePath('wheel/util.py') + >>> util.size + 859 + >>> util.dist + + >>> util.hash + + +Once you have the file, you can also read its contents:: + + >>> print(util.read_text()) + import base64 + import sys + ... + def as_bytes(s): + if isinstance(s, text_type): + return s.encode('utf-8') + return s + +In the case where the metadata file listing files +(RECORD or SOURCES.txt) is missing, ``files()`` will +return ``None``. The caller may wish to wrap calls to +``files()`` in `always_iterable +`_ +or otherwise guard against this condition if the target +distribution is not known to have the metadata present. + +.. _requirements: + +Distribution requirements +------------------------- + +To get the full set of requirements for a distribution, use the ``requires()`` +function:: + + >>> requires('wheel') + ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] + + +Distributions +============= + +While the above API is the most common and convenient usage, you can get all +of that information from the ``Distribution`` class. A ``Distribution`` is an +abstract object that represents the metadata for a Python package. You can +get the ``Distribution`` instance:: + + >>> from importlib_metadata import distribution + >>> dist = distribution('wheel') + +Thus, an alternative way to get the version number is through the +``Distribution`` instance:: + + >>> dist.version + '0.32.3' + +There are all kinds of additional metadata available on the ``Distribution`` +instance:: + + >>> d.metadata['Requires-Python'] + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + >>> d.metadata['License'] + 'MIT' + +The full set of available metadata is not described here. See :pep:`566` +for additional details. + + +Extending the search algorithm +============================== + +Because package metadata is not available through :data:`sys.path` searches, or +package loaders directly, the metadata for a package is found through import +system `finders`_. To find a distribution package's metadata, +``importlib.metadata`` queries the list of :term:`meta path finders ` on +:data:`sys.meta_path`. + +By default ``importlib_metadata`` installs a finder for distribution packages +found on the file system. This finder doesn't actually find any *packages*, +but it can find the packages' metadata. + +The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the +interface expected of finders by Python's import system. +``importlib_metadata`` extends this protocol by looking for an optional +``find_distributions`` callable on the finders from +:data:`sys.meta_path` and presents this extended interface as the +``DistributionFinder`` abstract base class, which defines this abstract +method:: + + @abc.abstractmethod + def find_distributions(context=DistributionFinder.Context()): + """Return an iterable of all Distribution instances capable of + loading the metadata for packages for the indicated ``context``. + """ + +The ``DistributionFinder.Context`` object provides ``.path`` and ``.name`` +properties indicating the path to search and names to match and may +supply other relevant context. + +What this means in practice is that to support finding distribution package +metadata in locations other than the file system, subclass +``Distribution`` and implement the abstract methods. Then from +a custom finder, return instances of this derived ``Distribution`` in the +``find_distributions()`` method. + + +.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points +.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api +.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders + + +.. rubric:: Footnotes + +.. [#f1] Technically, the returned distribution metadata object is an + :class:`email.message.EmailMessage` + instance, but this is an implementation detail, and not part of the + stable API. You should only use dictionary-like methods and syntax + to access the metadata contents. diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__init__.py b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92f9518a372c09bbc070a31804b287fc1066e21f GIT binary patch literal 168 zcmZ?b<>g`kg8$*FaUl9Jh=2h`Aj1KOi&=m~3PUi1CZpdKjm82Gz6zj*wXXa&= Z#K-FuRNmsS$<0qG%}KQbS@ju+834wCEKmRd literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/fixtures.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/fixtures.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75a47ee4aa6d8562ec33084c57bafb7eacd8ec21 GIT binary patch literal 6115 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_integration.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_integration.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9921220f2ce9892717675875d6fe5076f35f7d7e GIT binary patch literal 1193 zcmb7EOK;Oa5Z?78aniJ;sOkj{zHo@tI4w7%v??tKl|zJRC4>)H#=8ld_|@Gtf~dXF z{t!5F;RpH3segeB%p`6S5JF<3{X9FKnf>PB9IdZ639O&59=!Qx5b_-lx620M5Vm>_ zzzCx;@#jzK8?&WyUq}O=dz^t8Aa7 znvZ0t!TF3Uts=Yv7kcA9hOKr1=AM`If->`psx3`Ep=^QdRvKa(P#)=r_RuG&QVLX2$@4^Kuw6J*+xyc$p1Gd7C!!c&(PSbaaRA{8_Z!bp2 zTb&$P2YY{W`Ldeil1QA**(~{MPoT&rP~;brk?)*AxZ@D2hIjsrOzHIJ^bhc>RrtT; KQ1}^87xNc2{zugS literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_main.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_main.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7ff6cbf618f40ab21ae97844fa0468d393dcbb5 GIT binary patch literal 9511 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

ir(Z090ZJ89CHPlq)F|?rF zv}V2^+&_r;OBSngAEy=Yd$Q;zvm~p!vAvzmmhqAFUJ~7cUCd2+V!oXon!iKrBbt<& mmL6MVEsVu0WepX_d38x&KDT@cznoaPbbVemRCD>#;vWDn&+oAS literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_zip.cpython-37.pyc b/venv/lib/python3.7/site-packages/importlib_metadata/tests/__pycache__/test_zip.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff6007e483fa17617e664a97a330472e47466684 GIT binary patch literal 3049 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/data/example-21.12-py3.6.egg b/venv/lib/python3.7/site-packages/importlib_metadata/tests/data/example-21.12-py3.6.egg new file mode 100644 index 0000000000000000000000000000000000000000..cdb298a19b09d360d18f72a91e9dd70d7d7f54ec GIT binary patch literal 1497 zcmWIWW@Zs#U|`^2c;|fF@3nF0krp8DC=d$*v8%hguBV@yzkYx>gb~~8$aTm-!1cSO ztKNj9U1!!ka0&=sxZ<^c4f@#kCS*N+n7GL42YfZ! zEk5xYo$43J*3tWYGog!X+Wp$0%DLy;7$(q4-Im54c05E zC@DSV$kpt?s$Pc9T!_{7cXah`>i&=P4|_F+Kpf9t5RHa za$M}sCdvd)D?c%>-)L{RYwfgc?`B2cv%3AJhgrnPA#S40>GXa36wF*N9Z0I@t{X7}-NOjPvdHdANi9gtOG(X3u8hyg%*!qYIq)$P1FG&RzN!bcfw~-l zSQ1%xYF7CNi(Cm9!wASN{?g`J(4WS;b8`cyF z8-Lyrw2`}*5zU6XDV#xEK$AIuSPa>QlKg`BoYb<^9FW7jA%;CgcOSCvP!dX2W`S;T zVp=MSkE(!4OG@)#g(A=%5EcgF)QZI1f}B+S`1s7c%#!$cy@E<_Pv77ZzGu!{zOdy{ z5JS_VE0ZoQvSIytrR&I@ra2*{dT2gSDqd(m7iiKmAO?jM!o-5gB$9^ zB^mj7y2fTei;|6>J$69*1_n8CRa9r%0Q~F|js{Hj!lsY`)J@ zQc8Oq-tYt+X9_ybF(v)e?xXXfCeDbSFnQOcg&Zk-O+Qj6gv^>bdC~kSlOkglO`kk7 zJZfQdVxm}n+M@Zh=B?wux=dbv{>= 1.0 + Requires-Dist: pytest; extra == 'test' + """, + "RECORD": "mod.py,sha256=abc,20\n", + "entry_points.txt": """ + [entries] + main = mod:main + ns:sub = mod:main + """ + }, + "mod.py": """ + def main(): + print("hello world") + """, + } + + def setUp(self): + super(DistInfoPkg, self).setUp() + build_files(DistInfoPkg.files, self.site_dir) + + +class DistInfoPkgOffPath(SiteDir): + def setUp(self): + super(DistInfoPkgOffPath, self).setUp() + build_files(DistInfoPkg.files, self.site_dir) + + +class EggInfoPkg(OnSysPath, SiteDir): + files = { + "egginfo_pkg.egg-info": { + "PKG-INFO": """ + Name: egginfo-pkg + Author: Steven Ma + License: Unknown + Version: 1.0.0 + Classifier: Intended Audience :: Developers + Classifier: Topic :: Software Development :: Libraries + """, + "SOURCES.txt": """ + mod.py + egginfo_pkg.egg-info/top_level.txt + """, + "entry_points.txt": """ + [entries] + main = mod:main + """, + "requires.txt": """ + wheel >= 1.0; python_version >= "2.7" + [test] + pytest + """, + "top_level.txt": "mod\n" + }, + "mod.py": """ + def main(): + print("hello world") + """, + } + + def setUp(self): + super(EggInfoPkg, self).setUp() + build_files(EggInfoPkg.files, prefix=self.site_dir) + + +class EggInfoFile(OnSysPath, SiteDir): + files = { + "egginfo_file.egg-info": """ + Metadata-Version: 1.0 + Name: egginfo_file + Version: 0.1 + Summary: An example package + Home-page: www.example.com + Author: Eric Haffa-Vee + Author-email: eric@example.coms + License: UNKNOWN + Description: UNKNOWN + Platform: UNKNOWN + """, + } + + def setUp(self): + super(EggInfoFile, self).setUp() + build_files(EggInfoFile.files, prefix=self.site_dir) + + +def build_files(file_defs, prefix=pathlib.Path()): + """Build a set of files/directories, as described by the + + file_defs dictionary. Each key/value pair in the dictionary is + interpreted as a filename/contents pair. If the contents value is a + dictionary, a directory is created, and the dictionary interpreted + as the files within it, recursively. + + For example: + + {"README.txt": "A README file", + "foo": { + "__init__.py": "", + "bar": { + "__init__.py": "", + }, + "baz.py": "# Some code", + } + } + """ + for name, contents in file_defs.items(): + full_name = prefix / name + if isinstance(contents, dict): + full_name.mkdir() + build_files(contents, prefix=full_name) + else: + if isinstance(contents, bytes): + with full_name.open('wb') as f: + f.write(contents) + else: + with full_name.open('w') as f: + f.write(DALS(contents)) + + +def DALS(str): + "Dedent and left-strip" + return textwrap.dedent(str).lstrip() diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_api.py b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_api.py new file mode 100644 index 0000000..aa346dd --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_api.py @@ -0,0 +1,176 @@ +import re +import textwrap +import unittest + +from . import fixtures +from .. import ( + Distribution, PackageNotFoundError, __version__, distribution, + entry_points, files, metadata, requires, version, + ) + +try: + from collections.abc import Iterator +except ImportError: + from collections import Iterator # noqa: F401 + +try: + from builtins import str as text +except ImportError: + from __builtin__ import unicode as text + + +class APITests( + fixtures.EggInfoPkg, + fixtures.DistInfoPkg, + fixtures.EggInfoFile, + unittest.TestCase): + + version_pattern = r'\d+\.\d+(\.\d)?' + + def test_retrieves_version_of_self(self): + pkg_version = version('egginfo-pkg') + assert isinstance(pkg_version, text) + assert re.match(self.version_pattern, pkg_version) + + def test_retrieves_version_of_distinfo_pkg(self): + pkg_version = version('distinfo-pkg') + assert isinstance(pkg_version, text) + assert re.match(self.version_pattern, pkg_version) + + def test_for_name_does_not_exist(self): + with self.assertRaises(PackageNotFoundError): + distribution('does-not-exist') + + def test_for_top_level(self): + self.assertEqual( + distribution('egginfo-pkg').read_text('top_level.txt').strip(), + 'mod') + + def test_read_text(self): + top_level = [ + path for path in files('egginfo-pkg') + if path.name == 'top_level.txt' + ][0] + self.assertEqual(top_level.read_text(), 'mod\n') + + def test_entry_points(self): + entries = dict(entry_points()['entries']) + ep = entries['main'] + self.assertEqual(ep.value, 'mod:main') + self.assertEqual(ep.extras, []) + + def test_metadata_for_this_package(self): + md = metadata('egginfo-pkg') + assert md['author'] == 'Steven Ma' + assert md['LICENSE'] == 'Unknown' + assert md['Name'] == 'egginfo-pkg' + classifiers = md.get_all('Classifier') + assert 'Topic :: Software Development :: Libraries' in classifiers + + def test_importlib_metadata_version(self): + assert re.match(self.version_pattern, __version__) + + @staticmethod + def _test_files(files): + root = files[0].root + for file in files: + assert file.root == root + assert not file.hash or file.hash.value + assert not file.hash or file.hash.mode == 'sha256' + assert not file.size or file.size >= 0 + assert file.locate().exists() + assert isinstance(file.read_binary(), bytes) + if file.name.endswith('.py'): + file.read_text() + + def test_file_hash_repr(self): + try: + assertRegex = self.assertRegex + except AttributeError: + # Python 2 + assertRegex = self.assertRegexpMatches + + util = [ + p for p in files('distinfo-pkg') + if p.name == 'mod.py' + ][0] + assertRegex( + repr(util.hash), + '') + + def test_files_dist_info(self): + self._test_files(files('distinfo-pkg')) + + def test_files_egg_info(self): + self._test_files(files('egginfo-pkg')) + + def test_version_egg_info_file(self): + self.assertEqual(version('egginfo-file'), '0.1') + + def test_requires_egg_info_file(self): + requirements = requires('egginfo-file') + self.assertIsNone(requirements) + + def test_requires_egg_info(self): + deps = requires('egginfo-pkg') + assert len(deps) == 2 + assert any( + dep == 'wheel >= 1.0; python_version >= "2.7"' + for dep in deps + ) + + def test_requires_dist_info(self): + deps = requires('distinfo-pkg') + assert len(deps) == 2 + assert all(deps) + assert 'wheel >= 1.0' in deps + assert "pytest; extra == 'test'" in deps + + def test_more_complex_deps_requires_text(self): + requires = textwrap.dedent(""" + dep1 + dep2 + + [:python_version < "3"] + dep3 + + [extra1] + dep4 + + [extra2:python_version < "3"] + dep5 + """) + deps = sorted(Distribution._deps_from_requires_text(requires)) + expected = [ + 'dep1', + 'dep2', + 'dep3; python_version < "3"', + 'dep4; extra == "extra1"', + 'dep5; (python_version < "3") and extra == "extra2"', + ] + # It's important that the environment marker expression be + # wrapped in parentheses to avoid the following 'and' binding more + # tightly than some other part of the environment expression. + + assert deps == expected + + +class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase): + def test_find_distributions_specified_path(self): + dists = Distribution.discover(path=[str(self.site_dir)]) + assert any( + dist.metadata['Name'] == 'distinfo-pkg' + for dist in dists + ) + + def test_distribution_at_pathlib(self): + """Demonstrate how to load metadata direct from a directory. + """ + dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info' + dist = Distribution.at(dist_info_path) + assert dist.version == '1.0.0' + + def test_distribution_at_str(self): + dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info' + dist = Distribution.at(str(dist_info_path)) + assert dist.version == '1.0.0' diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_integration.py b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_integration.py new file mode 100644 index 0000000..11ed7dc --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_integration.py @@ -0,0 +1,22 @@ +import unittest +import packaging.requirements +import packaging.version + +from . import fixtures +from .. import version + + +class IntegrationTests(fixtures.DistInfoPkg, unittest.TestCase): + + def test_package_spec_installed(self): + """ + Illustrate the recommended procedure to determine if + a specified version of a package is installed. + """ + def is_installed(package_spec): + req = packaging.requirements.Requirement(package_spec) + return version(req.name) in req.specifier + + assert is_installed('distinfo-pkg==1.0') + assert is_installed('distinfo-pkg>=1.0,<2.0') + assert not is_installed('distinfo-pkg<1.0') diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_main.py b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_main.py new file mode 100644 index 0000000..cc2efda --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_main.py @@ -0,0 +1,224 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import re +import json +import pickle +import textwrap +import unittest +import importlib +import importlib_metadata + +from . import fixtures +from .. import ( + Distribution, EntryPoint, MetadataPathFinder, + PackageNotFoundError, distributions, + entry_points, metadata, version, + ) + +try: + from builtins import str as text +except ImportError: + from __builtin__ import unicode as text + + +class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): + version_pattern = r'\d+\.\d+(\.\d)?' + + def test_retrieves_version_of_self(self): + dist = Distribution.from_name('distinfo-pkg') + assert isinstance(dist.version, text) + assert re.match(self.version_pattern, dist.version) + + def test_for_name_does_not_exist(self): + with self.assertRaises(PackageNotFoundError): + Distribution.from_name('does-not-exist') + + def test_new_style_classes(self): + self.assertIsInstance(Distribution, type) + self.assertIsInstance(MetadataPathFinder, type) + + +class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): + def test_import_nonexistent_module(self): + # Ensure that the MetadataPathFinder does not crash an import of a + # non-existent module. + with self.assertRaises(ImportError): + importlib.import_module('does_not_exist') + + def test_resolve(self): + entries = dict(entry_points()['entries']) + ep = entries['main'] + self.assertEqual(ep.load().__name__, "main") + + def test_entrypoint_with_colon_in_name(self): + entries = dict(entry_points()['entries']) + ep = entries['ns:sub'] + self.assertEqual(ep.value, 'mod:main') + + def test_resolve_without_attr(self): + ep = EntryPoint( + name='ep', + value='importlib_metadata', + group='grp', + ) + assert ep.load() is importlib_metadata + + +class NameNormalizationTests( + fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): + @staticmethod + def pkg_with_dashes(site_dir): + """ + Create minimal metadata for a package with dashes + in the name (and thus underscores in the filename). + """ + metadata_dir = site_dir / 'my_pkg.dist-info' + metadata_dir.mkdir() + metadata = metadata_dir / 'METADATA' + with metadata.open('w') as strm: + strm.write('Version: 1.0\n') + return 'my-pkg' + + def test_dashes_in_dist_name_found_as_underscores(self): + """ + For a package with a dash in the name, the dist-info metadata + uses underscores in the name. Ensure the metadata loads. + """ + pkg_name = self.pkg_with_dashes(self.site_dir) + assert version(pkg_name) == '1.0' + + @staticmethod + def pkg_with_mixed_case(site_dir): + """ + Create minimal metadata for a package with mixed case + in the name. + """ + metadata_dir = site_dir / 'CherryPy.dist-info' + metadata_dir.mkdir() + metadata = metadata_dir / 'METADATA' + with metadata.open('w') as strm: + strm.write('Version: 1.0\n') + return 'CherryPy' + + def test_dist_name_found_as_any_case(self): + """ + Ensure the metadata loads when queried with any case. + """ + pkg_name = self.pkg_with_mixed_case(self.site_dir) + assert version(pkg_name) == '1.0' + assert version(pkg_name.lower()) == '1.0' + assert version(pkg_name.upper()) == '1.0' + + +class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): + @staticmethod + def pkg_with_non_ascii_description(site_dir): + """ + Create minimal metadata for a package with non-ASCII in + the description. + """ + metadata_dir = site_dir / 'portend.dist-info' + metadata_dir.mkdir() + metadata = metadata_dir / 'METADATA' + with metadata.open('w', encoding='utf-8') as fp: + fp.write('Description: pôrˈtend\n') + return 'portend' + + @staticmethod + def pkg_with_non_ascii_description_egg_info(site_dir): + """ + Create minimal metadata for an egg-info package with + non-ASCII in the description. + """ + metadata_dir = site_dir / 'portend.dist-info' + metadata_dir.mkdir() + metadata = metadata_dir / 'METADATA' + with metadata.open('w', encoding='utf-8') as fp: + fp.write(textwrap.dedent(""" + Name: portend + + pôrˈtend + """).lstrip()) + return 'portend' + + def test_metadata_loads(self): + pkg_name = self.pkg_with_non_ascii_description(self.site_dir) + meta = metadata(pkg_name) + assert meta['Description'] == 'pôrˈtend' + + def test_metadata_loads_egg_info(self): + pkg_name = self.pkg_with_non_ascii_description_egg_info(self.site_dir) + meta = metadata(pkg_name) + assert meta.get_payload() == 'pôrˈtend\n' + + +class DiscoveryTests(fixtures.EggInfoPkg, + fixtures.DistInfoPkg, + unittest.TestCase): + + def test_package_discovery(self): + dists = list(distributions()) + assert all( + isinstance(dist, Distribution) + for dist in dists + ) + assert any( + dist.metadata['Name'] == 'egginfo-pkg' + for dist in dists + ) + assert any( + dist.metadata['Name'] == 'distinfo-pkg' + for dist in dists + ) + + def test_invalid_usage(self): + with self.assertRaises(ValueError): + list(distributions(context='something', name='else')) + + +class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): + def test_egg_info(self): + # make an `EGG-INFO` directory that's unrelated + self.site_dir.joinpath('EGG-INFO').mkdir() + # used to crash with `IsADirectoryError` + with self.assertRaises(PackageNotFoundError): + version('unknown-package') + + def test_egg(self): + egg = self.site_dir.joinpath('foo-3.6.egg') + egg.mkdir() + with self.add_sys_path(egg): + with self.assertRaises(PackageNotFoundError): + version('foo') + + +class TestEntryPoints(unittest.TestCase): + def __init__(self, *args): + super(TestEntryPoints, self).__init__(*args) + self.ep = importlib_metadata.EntryPoint('name', 'value', 'group') + + def test_entry_point_pickleable(self): + revived = pickle.loads(pickle.dumps(self.ep)) + assert revived == self.ep + + def test_immutable(self): + """EntryPoints should be immutable""" + with self.assertRaises(AttributeError): + self.ep.name = 'badactor' + + def test_repr(self): + assert 'EntryPoint' in repr(self.ep) + assert 'name=' in repr(self.ep) + assert "'name'" in repr(self.ep) + + def test_hashable(self): + """EntryPoints should be hashable""" + hash(self.ep) + + def test_json_dump(self): + """ + json should not expect to be able to dump an EntryPoint + """ + with self.assertRaises(Exception): + json.dumps(self.ep) diff --git a/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_zip.py b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_zip.py new file mode 100644 index 0000000..8cbba63 --- /dev/null +++ b/venv/lib/python3.7/site-packages/importlib_metadata/tests/test_zip.py @@ -0,0 +1,70 @@ +import sys +import unittest + +from .. import distribution, entry_points, files, PackageNotFoundError, version + +try: + from importlib.resources import path +except ImportError: + from importlib_resources import path + +try: + from contextlib import ExitStack +except ImportError: + from contextlib2 import ExitStack + + +class TestZip(unittest.TestCase): + root = 'importlib_metadata.tests.data' + + def setUp(self): + # Find the path to the example-*.whl so we can add it to the front of + # sys.path, where we'll then try to find the metadata thereof. + self.resources = ExitStack() + self.addCleanup(self.resources.close) + wheel = self.resources.enter_context( + path(self.root, 'example-21.12-py3-none-any.whl')) + sys.path.insert(0, str(wheel)) + self.resources.callback(sys.path.pop, 0) + + def test_zip_version(self): + self.assertEqual(version('example'), '21.12') + + def test_zip_version_does_not_match(self): + with self.assertRaises(PackageNotFoundError): + version('definitely-not-installed') + + def test_zip_entry_points(self): + scripts = dict(entry_points()['console_scripts']) + entry_point = scripts['example'] + self.assertEqual(entry_point.value, 'example:main') + entry_point = scripts['Example'] + self.assertEqual(entry_point.value, 'example:main') + + def test_missing_metadata(self): + self.assertIsNone(distribution('example').read_text('does not exist')) + + def test_case_insensitive(self): + self.assertEqual(version('Example'), '21.12') + + def test_files(self): + for file in files('example'): + path = str(file.dist.locate_file(file)) + assert '.whl/' in path, path + + +class TestEgg(TestZip): + def setUp(self): + # Find the path to the example-*.egg so we can add it to the front of + # sys.path, where we'll then try to find the metadata thereof. + self.resources = ExitStack() + self.addCleanup(self.resources.close) + egg = self.resources.enter_context( + path(self.root, 'example-21.12-py3.6.egg')) + sys.path.insert(0, str(egg)) + self.resources.callback(sys.path.pop, 0) + + def test_files(self): + for file in files('example'): + path = str(file.dist.locate_file(file)) + assert '.egg/' in path, path diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/LICENSE b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/LICENSE new file mode 100644 index 0000000..0a523be --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Erik Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/METADATA b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/METADATA new file mode 100644 index 0000000..7d34487 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/METADATA @@ -0,0 +1,599 @@ +Metadata-Version: 2.1 +Name: more-itertools +Version: 8.1.0 +Summary: More routines for operating on iterables, beyond itertools +Home-page: https://github.com/erikrose/more-itertools +Author: Erik Rose +Author-email: erikrose@grinchcentral.com +License: MIT +Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.5 + +============== +More Itertools +============== + +.. image:: https://coveralls.io/repos/github/erikrose/more-itertools/badge.svg?branch=master + :target: https://coveralls.io/github/erikrose/more-itertools?branch=master + +Python's ``itertools`` library is a gem - you can compose elegant solutions +for a variety of problems with the functions it provides. In ``more-itertools`` +we collect additional building blocks, recipes, and routines for working with +Python iterables. + +---- + ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Grouping | `chunked `_, | +| | `ichunked `_, | +| | `sliced `_, | +| | `distribute `_, | +| | `divide `_, | +| | `split_at `_, | +| | `split_before `_, | +| | `split_after `_, | +| | `split_into `_, | +| | `split_when `_, | +| | `bucket `_, | +| | `unzip `_, | +| | `grouper `_, | +| | `partition `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Lookahead and lookback | `spy `_, | +| | `peekable `_, | +| | `seekable `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Windowing | `windowed `_, | +| | `substrings `_, | +| | `substrings_indexes `_, | +| | `stagger `_, | +| | `pairwise `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Augmenting | `count_cycle `_, | +| | `intersperse `_, | +| | `padded `_, | +| | `repeat_last `_, | +| | `adjacent `_, | +| | `groupby_transform `_, | +| | `padnone `_, | +| | `ncycles `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combining | `collapse `_, | +| | `sort_together `_, | +| | `interleave `_, | +| | `interleave_longest `_, | +| | `zip_offset `_, | +| | `dotproduct `_, | +| | `flatten `_, | +| | `roundrobin `_, | +| | `prepend `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Summarizing | `ilen `_, | +| | `unique_to_each `_, | +| | `sample `_, | +| | `consecutive_groups `_, | +| | `run_length `_, | +| | `map_reduce `_, | +| | `exactly_n `_, | +| | `all_equal `_, | +| | `first_true `_, | +| | `quantify `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Selecting | `islice_extended `_, | +| | `first `_, | +| | `last `_, | +| | `one `_, | +| | `only `_, | +| | `strip `_, | +| | `lstrip `_, | +| | `rstrip `_, | +| | `filter_except `_ | +| | `map_except `_ | +| | `nth_or_last `_, | +| | `nth `_, | +| | `take `_, | +| | `tail `_, | +| | `unique_everseen `_, | +| | `unique_justseen `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combinatorics | `distinct_permutations `_, | +| | `distinct_combinations `_, | +| | `circular_shifts `_, | +| | `partitions `_, | +| | `set_partitions `_, | +| | `powerset `_, | +| | `random_product `_, | +| | `random_permutation `_, | +| | `random_combination `_, | +| | `random_combination_with_replacement `_, | +| | `nth_combination `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Wrapping | `always_iterable `_, | +| | `always_reversible `_, | +| | `consumer `_, | +| | `with_iter `_, | +| | `iter_except `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Others | `locate `_, | +| | `rlocate `_, | +| | `replace `_, | +| | `numeric_range `_, | +| | `side_effect `_, | +| | `iterate `_, | +| | `difference `_, | +| | `make_decorator `_, | +| | `SequenceView `_, | +| | `time_limited `_, | +| | `consume `_, | +| | `tabulate `_, | +| | `repeatfunc `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +Getting started +=============== + +To get started, install the library with `pip `_: + +.. code-block:: shell + + pip install more-itertools + +The recipes from the `itertools docs `_ +are included in the top-level package: + +.. code-block:: python + + >>> from more_itertools import flatten + >>> iterable = [(0, 1), (2, 3)] + >>> list(flatten(iterable)) + [0, 1, 2, 3] + +Several new recipes are available as well: + +.. code-block:: python + + >>> from more_itertools import chunked + >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8] + >>> list(chunked(iterable, 3)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + >>> from more_itertools import spy + >>> iterable = (x * x for x in range(1, 6)) + >>> head, iterable = spy(iterable, n=3) + >>> list(head) + [1, 4, 9] + >>> list(iterable) + [1, 4, 9, 16, 25] + + + +For the full listing of functions, see the `API documentation `_. + +Development +=========== + +``more-itertools`` is maintained by `@erikrose `_ +and `@bbayles `_, with help from `many others `_. +If you have a problem or suggestion, please file a bug or pull request in this +repository. Thanks for contributing! + + +Version History +=============== + + + :noindex: + +8.1.0 +----- + +* Bug fixes + * partition works with ``pred=None`` again. (thanks to MSeifert04) + +* New itertools + * sample (thanks to tommyod) + * nth_or_last (thanks to d-ryzhikov) + +* Changes to existing itertools: + * The implementation for divide was improved. (thanks to jferard) + +8.0.2 +----- + +* Bug fixes + * The type stub files are now part of the wheel distribution (thanks to keisheiled) + +8.0.1 +----- + +* Bug fixes + * The type stub files now work for functions imported from the + root package (thanks to keisheiled) + +8.0.0 +----- + +* New itertools and other additions + * This library now ships type hints for use with mypy. + (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance) + * split_when (thanks to jferard) + * repeat_last (thanks to d-ryzhikov) + +* Changes to existing itertools: + * The implementation for set_partitions was improved. (thanks to jferard) + * partition was optimized for expensive predicates. (thanks to stevecj) + * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky) + * The implementation for difference was improved. (thanks to Jabbey92) + + +* Other changes + * Python 3.4 has reached its end of life and is no longer supported. + * Python 3.8 is officially supported. (thanks to jdufresne) + * The ``collate`` function has been deprecated. + It raises a ``DeprecationWarning`` if used, and will be removed in a future release. + * one and only now provide more informative error messages. (thanks to gabbard) + * Unit tests were moved outside of the main package (thanks to jdufresne) + * Various documentation fixes (thanks to kriomant, gabbard, jdufresne) + + +7.2.0 +----- + +* New itertools + * distinct_combinations + * set_partitions (thanks to kbarrett) + * filter_except + * map_except + +7.1.0 +----- + +* New itertools + * ichunked (thanks davebelais and youtux) + * only (thanks jaraco) + +* Changes to existing itertools: + * numeric_range now supports ranges specified by + ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests). + * difference now supports an *initial* keyword argument. + + +* Other changes + * Various documentation fixes (thanks raimon49, pylang) + +7.0.0 +----- + +* New itertools: + * time_limited + * partitions (thanks to rominf and Saluev) + * substrings_indexes (thanks to rominf) + +* Changes to existing itertools: + * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet) + +The major version update is due to the change in the default behavior of +collapse. It now treats ``bytes`` objects the same as ``str`` objects. +This aligns its behavior with always_iterable. + +.. code-block:: python + + >>> from more_itertools import collapse + >>> iterable = [[1, 2], b'345', [6]] + >>> print(list(collapse(iterable))) + [1, 2, b'345', 6] + +6.0.0 +----- + +* Major changes: + * Python 2.7 is no longer supported. The 5.0.0 release will be the last + version targeting Python 2.7. + * All future releases will target the active versions of Python 3. + As of 2019, those are Python 3.4 and above. + * The ``six`` library is no longer a dependency. + * The accumulate function is no longer part of this library. You + may import a better version from the standard ``itertools`` module. + +* Changes to existing itertools: + * The order of the parameters in grouper have changed to match + the latest recipe in the itertools documentation. Use of the old order + will be supported in this release, but emit a ``DeprecationWarning``. + The legacy behavior will be dropped in a future release. (thanks to jaraco) + * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values `_ at StackOverflow.) + * An unused parameter was removed from substrings. (thanks to pylang) + +* Other changes: + * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04) + * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk) + +5.0.0 +----- + +* New itertools: + * split_into (thanks to rovyko) + * unzip (thanks to bmintz) + * substrings (thanks to pylang) + +* Changes to existing itertools: + * ilen was optimized a bit (thanks to MSeifert04, achampion, and bmintz) + * first_true now returns ``None`` by default. This is the reason for the major version bump - see below. (thanks to sk and OJFord) + +* Other changes: + * Some code for old Python versions was removed (thanks to hugovk) + * Some documentation mistakes were corrected (thanks to belm0 and hugovk) + * Tests now run properly on 32-bit versions of Python (thanks to Millak) + * Newer versions of CPython and PyPy are now tested against + +The major version update is due to the change in the default return value of +first_true. It's now ``None``. + +.. code-block:: python + + >>> from more_itertools import first_true + >>> iterable = [0, '', False, [], ()] # All these are False + >>> answer = first_true(iterable) + >>> print(answer) + None + +4.3.0 +----- + +* New itertools: + * last (thanks to tmshn) + * replace (thanks to pylang) + * rlocate (thanks to jferard and pylang) + +* Improvements to existing itertools: + * locate can now search for multiple items + +* Other changes: + * The docs now include a nice table of tools (thanks MSeifert04) + +4.2.0 +----- + +* New itertools: + * map_reduce (thanks to pylang) + * prepend (from the `Python 3.7 docs `_) + +* Improvements to existing itertools: + * bucket now complies with PEP 479 (thanks to irmen) + +* Other changes: + * Python 3.7 is now supported (thanks to irmen) + * Python 3.3 is no longer supported + * The test suite no longer requires third-party modules to run + * The API docs now include links to source code + +4.1.0 +----- + +* New itertools: + * split_at (thanks to michael-celani) + * circular_shifts (thanks to hiqua) + * make_decorator - see the blog post `Yo, I heard you like decorators `_ + for a tour (thanks to pylang) + * always_reversible (thanks to michael-celani) + * nth_combination (from the `Python 3.7 docs `_) + +* Improvements to existing itertools: + * seekable now has an ``elements`` method to return cached items. + * The performance tradeoffs between roundrobin and + interleave_longest are now documented (thanks michael-celani, + pylang, and MSeifert04) + +4.0.1 +----- + +* No code changes - this release fixes how the docs display on PyPI. + +4.0.0 +----- + +* New itertools: + * consecutive_groups (Based on the example in the `Python 2.4 docs `_) + * seekable (If you're looking for how to "reset" an iterator, + you're in luck!) + * exactly_n (thanks to michael-celani) + * run_length.encode and run_length.decode + * difference + +* Improvements to existing itertools: + * The number of items between filler elements in intersperse can + now be specified (thanks to pylang) + * distinct_permutations and peekable got some minor + adjustments (thanks to MSeifert04) + * always_iterable now returns an iterator object. It also now + allows different types to be considered iterable (thanks to jaraco) + * bucket can now limit the keys it stores in memory + * one now allows for custom exceptions (thanks to kalekundert) + +* Other changes: + * A few typos were fixed (thanks to EdwardBetts) + * All tests can now be run with ``python setup.py test`` + +The major version update is due to the change in the return value of always_iterable. +It now always returns iterator objects: + +.. code-block:: python + + >>> from more_itertools import always_iterable + # Non-iterable objects are wrapped with iter(tuple(obj)) + >>> always_iterable(12345) + + >>> list(always_iterable(12345)) + [12345] + # Iterable objects are wrapped with iter() + >>> always_iterable([1, 2, 3, 4, 5]) + + +3.2.0 +----- + +* New itertools: + * lstrip, rstrip, and strip + (thanks to MSeifert04 and pylang) + * islice_extended +* Improvements to existing itertools: + * Some bugs with slicing peekable-wrapped iterables were fixed + +3.1.0 +----- + +* New itertools: + * numeric_range (Thanks to BebeSparkelSparkel and MSeifert04) + * count_cycle (Thanks to BebeSparkelSparkel) + * locate (Thanks to pylang and MSeifert04) +* Improvements to existing itertools: + * A few itertools are now slightly faster due to some function + optimizations. (Thanks to MSeifert04) +* The docs have been substantially revised with installation notes, + categories for library functions, links, and more. (Thanks to pylang) + + +3.0.0 +----- + +* Removed itertools: + * ``context`` has been removed due to a design flaw - see below for + replacement options. (thanks to NeilGirdhar) +* Improvements to existing itertools: + * ``side_effect`` now supports ``before`` and ``after`` keyword + arguments. (Thanks to yardsale8) +* PyPy and PyPy3 are now supported. + +The major version change is due to the removal of the ``context`` function. +Replace it with standard ``with`` statement context management: + +.. code-block:: python + + # Don't use context() anymore + file_obj = StringIO() + consume(print(x, file=f) for f in context(file_obj) for x in u'123') + + # Use a with statement instead + file_obj = StringIO() + with file_obj as f: + consume(print(x, file=f) for x in u'123') + +2.6.0 +----- + +* New itertools: + * ``adjacent`` and ``groupby_transform`` (Thanks to diazona) + * ``always_iterable`` (Thanks to jaraco) + * (Removed in 3.0.0) ``context`` (Thanks to yardsale8) + * ``divide`` (Thanks to mozbhearsum) +* Improvements to existing itertools: + * ``ilen`` is now slightly faster. (Thanks to wbolster) + * ``peekable`` can now prepend items to an iterable. (Thanks to diazona) + +2.5.0 +----- + +* New itertools: + * ``distribute`` (Thanks to mozbhearsum and coady) + * ``sort_together`` (Thanks to clintval) + * ``stagger`` and ``zip_offset`` (Thanks to joshbode) + * ``padded`` +* Improvements to existing itertools: + * ``peekable`` now handles negative indexes and slices with negative + components properly. + * ``intersperse`` is now slightly faster. (Thanks to pylang) + * ``windowed`` now accepts a ``step`` keyword argument. + (Thanks to pylang) +* Python 3.6 is now supported. + +2.4.1 +----- + +* Move docs 100% to readthedocs.io. + +2.4 +----- + +* New itertools: + * ``accumulate``, ``all_equal``, ``first_true``, ``partition``, and + ``tail`` from the itertools documentation. + * ``bucket`` (Thanks to Rosuav and cvrebert) + * ``collapse`` (Thanks to abarnet) + * ``interleave`` and ``interleave_longest`` (Thanks to abarnet) + * ``side_effect`` (Thanks to nvie) + * ``sliced`` (Thanks to j4mie and coady) + * ``split_before`` and ``split_after`` (Thanks to astronouth7303) + * ``spy`` (Thanks to themiurgo and mathieulongtin) +* Improvements to existing itertools: + * ``chunked`` is now simpler and more friendly to garbage collection. + (Contributed by coady, with thanks to piskvorky) + * ``collate`` now delegates to ``heapq.merge`` when possible. + (Thanks to kmike and julianpistorius) + * ``peekable``-wrapped iterables are now indexable and sliceable. + Iterating through ``peekable``-wrapped iterables is also faster. + * ``one`` and ``unique_to_each`` have been simplified. + (Thanks to coady) + + +2.3 +----- + +* Added ``one`` from ``jaraco.util.itertools``. (Thanks, jaraco!) +* Added ``distinct_permutations`` and ``unique_to_each``. (Contributed by + bbayles) +* Added ``windowed``. (Contributed by bbayles, with thanks to buchanae, + jaraco, and abarnert) +* Simplified the implementation of ``chunked``. (Thanks, nvie!) +* Python 3.5 is now supported. Python 2.6 is no longer supported. +* Python 3 is now supported directly; there is no 2to3 step. + +2.2 +----- + +* Added ``iterate`` and ``with_iter``. (Thanks, abarnert!) + +2.1 +----- + +* Added (tested!) implementations of the recipes from the itertools + documentation. (Thanks, Chris Lonnen!) +* Added ``ilen``. (Thanks for the inspiration, Matt Basta!) + +2.0 +----- + +* ``chunked`` now returns lists rather than tuples. After all, they're + homogeneous. This slightly backward-incompatible change is the reason for + the major version bump. +* Added ``@consumer``. +* Improved test machinery. + +1.1 +----- + +* Added ``first`` function. +* Added Python 3 support. +* Added a default arg to ``peekable.peek()``. +* Noted how to easily test whether a peekable iterator is exhausted. +* Rewrote documentation. + +1.0 +----- + +* Initial release, with ``collate``, ``peekable``, and ``chunked``. Could + really use better docs. + diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/RECORD b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/RECORD new file mode 100644 index 0000000..2b6fc20 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/RECORD @@ -0,0 +1,16 @@ +more_itertools-8.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +more_itertools-8.1.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053 +more_itertools-8.1.0.dist-info/METADATA,sha256=detmvnZebHWCA_jlFzMUB9cMt4YlDBA3UZb4R9jBKv8,41479 +more_itertools-8.1.0.dist-info/RECORD,, +more_itertools-8.1.0.dist-info/WHEEL,sha256=S8S5VL-stOTSZDYxHyf0KP7eds0J72qrK0Evu3TfyAY,92 +more_itertools-8.1.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15 +more_itertools/__init__.py,sha256=L6vgDVu53ct8RRCYu_3j-a3hRqO75I49OAKCEGwoOAU,82 +more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43 +more_itertools/__pycache__/__init__.cpython-37.pyc,, +more_itertools/__pycache__/more.cpython-37.pyc,, +more_itertools/__pycache__/recipes.cpython-37.pyc,, +more_itertools/more.py,sha256=phy8iS3WSLkxqjOIgeH0mJ3AuUGXzJiUDISccywB8kA,88410 +more_itertools/more.pyi,sha256=lWJxG2Qs1NJHvHtkuWD9HkEtjgRqZn8Wk0kTLcC2xcg,11496 +more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +more_itertools/recipes.py,sha256=Rbb0ezfaLydmcMNI2DuC1CQYH5jR5fq2Pmhd2wEafIs,15130 +more_itertools/recipes.pyi,sha256=eNpP03Jl1vaVf83tvAnx3oxeErKVYShsTuiNAeoyeNI,3342 diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/WHEEL b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/WHEEL new file mode 100644 index 0000000..c57a597 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..a5035be --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools-8.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +more_itertools diff --git a/venv/lib/python3.7/site-packages/more_itertools/__init__.py b/venv/lib/python3.7/site-packages/more_itertools/__init__.py new file mode 100644 index 0000000..a768952 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/__init__.py @@ -0,0 +1,4 @@ +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '8.1.0' diff --git a/venv/lib/python3.7/site-packages/more_itertools/__init__.pyi b/venv/lib/python3.7/site-packages/more_itertools/__init__.pyi new file mode 100644 index 0000000..96f6e36 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/venv/lib/python3.7/site-packages/more_itertools/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/more_itertools/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..929981225ec217c952df07c7dbffcb88ed2e6832 GIT binary patch literal 220 zcmZ?b<>g`k0=5X%xF8_?7{q}ACLqHBh>InFL<&O+V-7> z(wdC77`3WcE%Xfa4E!{iqgZnDi&CT5i&B#_3sQ@3amUA(r4|)u=I6!7uVg4<0qOt~ zznt|;atrheG7EGw^NLFnb8>V`3oO$z4U-G?K_=*CmZTPy&r cyk0@&Ee@O9{FKt1R6CGMia~~RFmf;f0QN68+W-In literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/more_itertools/__pycache__/more.cpython-37.pyc b/venv/lib/python3.7/site-packages/more_itertools/__pycache__/more.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c356ee59beaf6de22fd19973dcaabfb2187a7164 GIT binary patch literal 83394 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%0D}OGydJn(h_Oqh7Y3Al8X^K@nlQxatl3SDeP%s(o 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/more_itertools/__pycache__/recipes.cpython-37.pyc b/venv/lib/python3.7/site-packages/more_itertools/__pycache__/recipes.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93cafa3a90720bb8d60b45ceabb284496989298c GIT binary patch literal 16666 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𓗺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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.7/site-packages/more_itertools/more.py b/venv/lib/python3.7/site-packages/more_itertools/more.py new file mode 100644 index 0000000..67ec503 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/more.py @@ -0,0 +1,2815 @@ +import warnings +from collections import Counter, defaultdict, deque +from collections.abc import Sequence +from functools import partial, wraps +from heapq import merge, heapify, heapreplace, heappop +from itertools import ( + chain, + compress, + count, + cycle, + dropwhile, + groupby, + islice, + repeat, + starmap, + takewhile, + tee, + zip_longest, +) +from math import exp, floor, log +from operator import gt, itemgetter, lt, sub +from random import random, randrange, uniform +from sys import maxsize +from time import monotonic + +from .recipes import consume, flatten, powerset, take, unique_everseen + +__all__ = [ + 'adjacent', + 'always_iterable', + 'always_reversible', + 'bucket', + 'chunked', + 'circular_shifts', + 'collapse', + 'collate', + 'consecutive_groups', + 'consumer', + 'count_cycle', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'exactly_n', + 'filter_except', + 'first', + 'groupby_transform', + 'ilen', + 'interleave_longest', + 'interleave', + 'intersperse', + 'islice_extended', + 'iterate', + 'ichunked', + 'last', + 'locate', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_reduce', + 'nth_or_last', + 'numeric_range', + 'one', + 'only', + 'padded', + 'partitions', + 'set_partitions', + 'peekable', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'SequenceView', + 'side_effect', + 'sliced', + 'sort_together', + 'split_at', + 'split_after', + 'split_before', + 'split_when', + 'split_into', + 'spy', + 'stagger', + 'strip', + 'substrings', + 'substrings_indexes', + 'time_limited', + 'unique_to_each', + 'unzip', + 'windowed', + 'with_iter', + 'zip_offset', +] + +_marker = object() + + +def chunked(iterable, n): + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + If the length of *iterable* is not evenly divisible by *n*, the last + returned list will be shorter: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + :func:`chunked` is useful for splitting up a computation on a large number + of keys into batches, to be pickled and sent off to worker processes. One + example is operations on rows in MySQL, which does not implement + server-side cursors properly and would otherwise load the entire dataset + into RAM on the client. + + """ + return iter(partial(take, n, iter(iterable)), []) + + +def first(iterable, default=_marker): + """Return the first item of *iterable*, or *default* if *iterable* is + empty. + + >>> first([0, 1, 2, 3]) + 0 + >>> first([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + + :func:`first` is useful when you have a generator of expensive-to-retrieve + values and want any arbitrary one. It is marginally shorter than + ``next(iter(iterable), default)``. + + """ + try: + return next(iter(iterable)) + except StopIteration: + # I'm on the edge about raising ValueError instead of StopIteration. At + # the moment, ValueError wins, because the caller could conceivably + # want to do something different with flow control when I raise the + # exception, and it's weird to explicitly catch StopIteration. + if default is _marker: + raise ValueError( + 'first() was called on an empty iterable, and no ' + 'default value was provided.' + ) + return default + + +def last(iterable, default=_marker): + """Return the last item of *iterable*, or *default* if *iterable* is + empty. + + >>> last([0, 1, 2, 3]) + 3 + >>> last([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + try: + try: + # Try to access the last item directly + return iterable[-1] + except (TypeError, AttributeError, KeyError): + # If not slice-able, iterate entirely using length-1 deque + return deque(iterable, maxlen=1)[0] + except IndexError: # If the iterable was empty + if default is _marker: + raise ValueError( + 'last() was called on an empty iterable, and no ' + 'default value was provided.' + ) + return default + + +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: + """Wrap an iterator to allow lookahead and prepending elements. + + Call :meth:`peek` on the result to get the value that will be returned + by :func:`next`. This won't advance the iterator: + + >>> p = peekable(['a', 'b']) + >>> p.peek() + 'a' + >>> next(p) + 'a' + + Pass :meth:`peek` a default value to return that instead of raising + ``StopIteration`` when the iterator is exhausted. + + >>> p = peekable([]) + >>> p.peek('hi') + 'hi' + + peekables also offer a :meth:`prepend` method, which "inserts" items + at the head of the iterable: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> p.peek() + 11 + >>> list(p) + [11, 12, 1, 2, 3] + + peekables can be indexed. Index 0 is the item that will be returned by + :func:`next`, index 1 is the item after that, and so on: + The values up to the given index will be cached. + + >>> p = peekable(['a', 'b', 'c', 'd']) + >>> p[0] + 'a' + >>> p[1] + 'b' + >>> next(p) + 'a' + + Negative indexes are supported, but be aware that they will cache the + remaining items in the source iterator, which may require significant + storage. + + To check whether a peekable is exhausted, check its truth value: + + >>> p = peekable(['a', 'b']) + >>> if p: # peekable has items + ... list(p) + ['a', 'b'] + >>> if not p: # peekable is exhaused + ... list(p) + [] + + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self._cache = deque() + + def __iter__(self): + return self + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + """Return the item that will be next returned from ``next()``. + + Return ``default`` if there are no items left. If ``default`` is not + provided, raise ``StopIteration``. + + """ + if not self._cache: + try: + self._cache.append(next(self._it)) + except StopIteration: + if default is _marker: + raise + return default + return self._cache[0] + + def prepend(self, *items): + """Stack up items to be the next ones returned from ``next()`` or + ``self.peek()``. The items will be returned in + first in, first out order:: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> list(p) + [11, 12, 1, 2, 3] + + It is possible, by prepending items, to "resurrect" a peekable that + previously raised ``StopIteration``. + + >>> p = peekable([]) + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + >>> p.prepend(1) + >>> next(p) + 1 + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + + """ + self._cache.extendleft(reversed(items)) + + def __next__(self): + if self._cache: + return self._cache.popleft() + + return next(self._it) + + def _get_slice(self, index): + # Normalize the slice's arguments + step = 1 if (index.step is None) else index.step + if step > 0: + start = 0 if (index.start is None) else index.start + stop = maxsize if (index.stop is None) else index.stop + elif step < 0: + start = -1 if (index.start is None) else index.start + stop = (-maxsize - 1) if (index.stop is None) else index.stop + else: + raise ValueError('slice step cannot be zero') + + # If either the start or stop index is negative, we'll need to cache + # the rest of the iterable in order to slice from the right side. + if (start < 0) or (stop < 0): + self._cache.extend(self._it) + # Otherwise we'll need to find the rightmost index and cache to that + # point. + else: + n = min(max(start, stop) + 1, maxsize) + cache_len = len(self._cache) + if n >= cache_len: + self._cache.extend(islice(self._it, n - cache_len)) + + return list(self._cache)[index] + + def __getitem__(self, index): + if isinstance(index, slice): + return self._get_slice(index) + + cache_len = len(self._cache) + if index < 0: + self._cache.extend(self._it) + elif index >= cache_len: + self._cache.extend(islice(self._it, index + 1 - cache_len)) + + return self._cache[index] + + +def collate(*iterables, **kwargs): + """Return a sorted merge of the items from each of several already-sorted + *iterables*. + + >>> list(collate('ACDZ', 'AZ', 'JKL')) + ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] + + Works lazily, keeping only the next value from each iterable in memory. Use + :func:`collate` to, for example, perform a n-way mergesort of items that + don't fit in memory. + + If a *key* function is specified, the iterables will be sorted according + to its result: + + >>> key = lambda s: int(s) # Sort by numeric value, not by string + >>> list(collate(['1', '10'], ['2', '11'], key=key)) + ['1', '2', '10', '11'] + + + If the *iterables* are sorted in descending order, set *reverse* to + ``True``: + + >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) + [5, 4, 3, 2, 1, 0] + + If the elements of the passed-in iterables are out of order, you might get + unexpected results. + + On Python 3.5+, this function is an alias for :func:`heapq.merge`. + + """ + warnings.warn( + "collate is no longer part of more_itertools, use heapq.merge", + DeprecationWarning, + ) + return merge(*iterables, **kwargs) + + +def consumer(func): + """Decorator that automatically advances a PEP-342-style "reverse iterator" + to its first yield point so you don't have to call ``next()`` on it + manually. + + >>> @consumer + ... def tally(): + ... i = 0 + ... while True: + ... print('Thing number %s is %s.' % (i, (yield))) + ... i += 1 + ... + >>> t = tally() + >>> t.send('red') + Thing number 0 is red. + >>> t.send('fish') + Thing number 1 is fish. + + Without the decorator, you would have to call ``next(t)`` before + ``t.send()`` could be used. + + """ + + @wraps(func) + def wrapper(*args, **kwargs): + gen = func(*args, **kwargs) + next(gen) + return gen + + return wrapper + + +def ilen(iterable): + """Return the number of items in *iterable*. + + >>> ilen(x for x in range(1000000) if x % 3 == 0) + 333334 + + This consumes the iterable, so handle with care. + + """ + # This approach was selected because benchmarks showed it's likely the + # fastest of the known implementations at the time of writing. + # See GitHub tracker: #236, #230. + counter = count() + deque(zip(iterable, counter), maxlen=0) + return next(counter) + + +def iterate(func, start): + """Return ``start``, ``func(start)``, ``func(func(start))``, ... + + >>> from itertools import islice + >>> list(islice(iterate(lambda x: 2*x, 1), 10)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + """ + while True: + yield start + start = func(start) + + +def with_iter(context_manager): + """Wrap an iterable in a ``with`` statement, so it closes once exhausted. + + For example, this will close the file when the iterator is exhausted:: + + upper_lines = (line.upper() for line in with_iter(open('foo'))) + + Any context manager which returns an iterable is a candidate for + ``with_iter``. + + """ + with context_manager as iterable: + yield from iterable + + +def one(iterable, too_short=None, too_long=None): + """Return the first item from *iterable*, which is expected to contain only + that item. Raise an exception if *iterable* is empty or has more than one + item. + + :func:`one` is useful for ensuring that an iterable contains only one item. + For example, it can be used to retrieve the result of a database query + that is expected to return a single row. + + If *iterable* is empty, ``ValueError`` will be raised. You may specify a + different exception with the *too_short* keyword: + + >>> it = [] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (expected 1)' + >>> too_short = IndexError('too few items') + >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + IndexError: too few items + + Similarly, if *iterable* contains more than one item, ``ValueError`` will + be raised. You may specify a different exception with the *too_long* + keyword: + + >>> it = ['too', 'many'] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. + >>> too_long = RuntimeError + >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + Note that :func:`one` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. + + """ + it = iter(iterable) + + try: + first_value = next(it) + except StopIteration: + raise too_short or ValueError('too few items in iterable (expected 1)') + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def distinct_permutations(iterable): + """Yield successive distinct permutations of the elements in *iterable*. + + >>> sorted(distinct_permutations([1, 0, 1])) + [(0, 1, 1), (1, 0, 1), (1, 1, 0)] + + Equivalent to ``set(permutations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + Duplicate permutations arise when there are duplicated elements in the + input iterable. The number of items returned is + `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of + items input, and each `x_i` is the count of a distinct item in the input + sequence. + + """ + + def make_new_permutations(pool, e): + """Internal helper function. + The output permutations are built up by adding element *e* to the + current *permutations* at every possible position. + The key idea is to keep repeated elements (reverse) ordered: + if e1 == e2 and e1 is before e2 in the iterable, then all permutations + with e1 before e2 are ignored. + + """ + for perm in pool: + for j in range(len(perm)): + yield perm[:j] + (e,) + perm[j:] + if perm[j] == e: + break + else: + yield perm + (e,) + + permutations = [()] + for e in iterable: + permutations = make_new_permutations(permutations, e) + + return (tuple(t) for t in permutations) + + +def intersperse(e, iterable, n=1): + """Intersperse filler element *e* among the items in *iterable*, leaving + *n* items between each filler element. + + >>> list(intersperse('!', [1, 2, 3, 4, 5])) + [1, '!', 2, '!', 3, '!', 4, '!', 5] + + >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) + [1, 2, None, 3, 4, None, 5] + + """ + if n == 0: + raise ValueError('n must be > 0') + elif n == 1: + # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... + return islice(interleave(repeat(e), iterable), 1, None) + else: + # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... + # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... + # flatten(...) -> x_0, x_1, e, x_2, x_3... + filler = repeat([e]) + chunks = chunked(iterable, n) + return flatten(islice(interleave(filler, chunks), 1, None)) + + +def unique_to_each(*iterables): + """Return the elements from each of the input iterables that aren't in the + other input iterables. + + For example, suppose you have a set of packages, each with a set of + dependencies:: + + {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} + + If you remove one package, which dependencies can also be removed? + + If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not + associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for + ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: + + >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) + [['A'], ['C'], ['D']] + + If there are duplicates in one input iterable that aren't in the others + they will be duplicated in the output. Input order is preserved:: + + >>> unique_to_each("mississippi", "missouri") + [['p', 'p'], ['o', 'u', 'r']] + + It is assumed that the elements of each iterable are hashable. + + """ + pool = [list(it) for it in iterables] + counts = Counter(chain.from_iterable(map(set, pool))) + uniques = {element for element in counts if counts[element] == 1} + return [list(filter(uniques.__contains__, it)) for it in pool] + + +def windowed(seq, n, fillvalue=None, step=1): + """Return a sliding window of width *n* over the given iterable. + + >>> all_windows = windowed([1, 2, 3, 4, 5], 3) + >>> list(all_windows) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + When the window is larger than the iterable, *fillvalue* is used in place + of missing values:: + + >>> list(windowed([1, 2, 3], 4)) + [(1, 2, 3, None)] + + Each window will advance in increments of *step*: + + >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) + [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] + + """ + if n < 0: + raise ValueError('n must be >= 0') + if n == 0: + yield tuple() + return + if step < 1: + raise ValueError('step must be >= 1') + + it = iter(seq) + window = deque([], n) + append = window.append + + # Initial deque fill + for _ in range(n): + append(next(it, fillvalue)) + yield tuple(window) + + # Appending new items to the right causes old items to fall off the left + i = 0 + for item in it: + append(item) + i = (i + 1) % step + if i % step == 0: + yield tuple(window) + + # If there are items from the iterable in the window, pad with the given + # value and emit them. + if (i % step) and (step - i < n): + for _ in range(step - i): + append(fillvalue) + yield tuple(window) + + +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iter(iterable): + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: + """Wrap *iterable* and return an object that buckets it iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) + + +def spy(iterable, n=1): + """Return a 2-tuple with a list containing the first *n* elements of + *iterable*, and an iterator with the same items as *iterable*. + This allows you to "look ahead" at the items in the iterable without + advancing it. + + There is one item in the list by default: + + >>> iterable = 'abcdefg' + >>> head, iterable = spy(iterable) + >>> head + ['a'] + >>> list(iterable) + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + You may use unpacking to retrieve items instead of lists: + + >>> (head,), iterable = spy('abcdefg') + >>> head + 'a' + >>> (first, second), iterable = spy('abcdefg', 2) + >>> first + 'a' + >>> second + 'b' + + The number of items requested can be larger than the number of items in + the iterable: + + >>> iterable = [1, 2, 3, 4, 5] + >>> head, iterable = spy(iterable, 10) + >>> head + [1, 2, 3, 4, 5] + >>> list(iterable) + [1, 2, 3, 4, 5] + + """ + it = iter(iterable) + head = take(n, it) + + return head, chain(head, it) + + +def interleave(*iterables): + """Return a new iterable yielding from each iterable in turn, + until the shortest is exhausted. + + >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7] + + For a version that doesn't terminate after the shortest iterable is + exhausted, see :func:`interleave_longest`. + + """ + return chain.from_iterable(zip(*iterables)) + + +def interleave_longest(*iterables): + """Return a new iterable yielding from each iterable in turn, + skipping any that are exhausted. + + >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7, 3, 8] + + This function produces the same output as :func:`roundrobin`, but may + perform better for some inputs (in particular when the number of iterables + is large). + + """ + i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) + return (x for x in i if x is not _marker) + + +def collapse(iterable, base_type=None, levels=None): + """Flatten an iterable with multiple levels of nesting (e.g., a list of + lists of tuples) into non-iterable types. + + >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] + >>> list(collapse(iterable)) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and + will not be collapsed. + + To avoid collapsing other types, specify *base_type*: + + >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] + >>> list(collapse(iterable, base_type=tuple)) + ['ab', ('cd', 'ef'), 'gh', 'ij'] + + Specify *levels* to stop flattening after a certain level: + + >>> iterable = [('a', ['b']), ('c', ['d'])] + >>> list(collapse(iterable)) # Fully flattened + ['a', 'b', 'c', 'd'] + >>> list(collapse(iterable, levels=1)) # Only one level flattened + ['a', ['b'], 'c', ['d']] + + """ + + def walk(node, level): + if ( + ((levels is not None) and (level > levels)) + or isinstance(node, (str, bytes)) + or ((base_type is not None) and isinstance(node, base_type)) + ): + yield node + return + + try: + tree = iter(node) + except TypeError: + yield node + return + else: + for child in tree: + yield from walk(child, level + 1) + + yield from walk(iterable, 0) + + +def side_effect(func, iterable, chunk_size=None, before=None, after=None): + """Invoke *func* on each item in *iterable* (or on each *chunk_size* group + of items) before yielding the item. + + `func` must be a function that takes a single argument. Its return value + will be discarded. + + *before* and *after* are optional functions that take no arguments. They + will be executed before iteration starts and after it ends, respectively. + + `side_effect` can be used for logging, updating progress bars, or anything + that is not functionally "pure." + + Emitting a status message: + + >>> from more_itertools import consume + >>> func = lambda item: print('Received {}'.format(item)) + >>> consume(side_effect(func, range(2))) + Received 0 + Received 1 + + Operating on chunks of items: + + >>> pair_sums = [] + >>> func = lambda chunk: pair_sums.append(sum(chunk)) + >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) + [0, 1, 2, 3, 4, 5] + >>> list(pair_sums) + [1, 5, 9] + + Writing to a file-like object: + + >>> from io import StringIO + >>> from more_itertools import consume + >>> f = StringIO() + >>> func = lambda x: print(x, file=f) + >>> before = lambda: print(u'HEADER', file=f) + >>> after = f.close + >>> it = [u'a', u'b', u'c'] + >>> consume(side_effect(func, it, before=before, after=after)) + >>> f.closed + True + + """ + try: + if before is not None: + before() + + if chunk_size is None: + for item in iterable: + func(item) + yield item + else: + for chunk in chunked(iterable, chunk_size): + func(chunk) + yield from chunk + finally: + if after is not None: + after() + + +def sliced(seq, n): + """Yield slices of length *n* from the sequence *seq*. + + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] + + If the length of the sequence is not divisible by the requested slice + length, the last slice will be shorter. + + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + This function will only work for iterables that support slicing. + For non-sliceable iterables, see :func:`chunked`. + + """ + return takewhile(bool, (seq[i : i + n] for i in count(0, n))) + + +def split_at(iterable, pred): + """Yield lists of items from *iterable*, where each list is delimited by + an item where callable *pred* returns ``True``. The lists do not include + the delimiting items. + + >>> list(split_at('abcdcba', lambda x: x == 'b')) + [['a'], ['c', 'd', 'c'], ['a']] + + >>> list(split_at(range(10), lambda n: n % 2 == 1)) + [[0], [2], [4], [6], [8], []] + """ + buf = [] + for item in iterable: + if pred(item): + yield buf + buf = [] + else: + buf.append(item) + yield buf + + +def split_before(iterable, pred): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: + + >>> list(split_before('OneTwo', lambda s: s.isupper())) + [['O', 'n', 'e'], ['T', 'w', 'o']] + + >>> list(split_before(range(10), lambda n: n % 3 == 0)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + + """ + buf = [] + for item in iterable: + if pred(item) and buf: + yield buf + buf = [] + buf.append(item) + yield buf + + +def split_after(iterable, pred): + """Yield lists of items from *iterable*, where each list ends with an + item where callable *pred* returns ``True``: + + >>> list(split_after('one1two2', lambda s: s.isdigit())) + [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] + + >>> list(split_after(range(10), lambda n: n % 3 == 0)) + [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + + """ + buf = [] + for item in iterable: + buf.append(item) + if pred(item) and buf: + yield buf + buf = [] + if buf: + yield buf + + +def split_when(iterable, pred): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + """ + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + buf = [] + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that itertools.slice + does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + +def padded(iterable, fillvalue=None, n=None, next_multiple=False): + """Yield the elements from *iterable*, followed by *fillvalue*, such that + at least *n* items are emitted. + + >>> list(padded([1, 2, 3], '?', 5)) + [1, 2, 3, '?', '?'] + + If *next_multiple* is ``True``, *fillvalue* will be emitted until the + number of items emitted is a multiple of *n*:: + + >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) + [1, 2, 3, 4, None, None] + + If *n* is ``None``, *fillvalue* will be emitted indefinitely. + + """ + it = iter(iterable) + if n is None: + yield from chain(it, repeat(fillvalue)) + elif n < 1: + raise ValueError('n must be at least 1') + else: + item_count = 0 + for item in it: + yield item + item_count += 1 + + remaining = (n - item_count) % n if next_multiple else n - item_count + for _ in range(remaining): + yield fillvalue + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + +def distribute(n, iterable): + """Distribute the items from *iterable* among *n* smaller iterables. + + >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 3, 5] + >>> list(group_2) + [2, 4, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 4, 7], [2, 5], [3, 6]] + + If the length of *iterable* is smaller than *n*, then the last returned + iterables will be empty: + + >>> children = distribute(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function uses :func:`itertools.tee` and may require significant + storage. If you need the order items in the smaller iterables to match the + original iterable, see :func:`divide`. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + children = tee(iterable, n) + return [islice(it, index, None, n) for index, it in enumerate(children)] + + +def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): + """Yield tuples whose elements are offset from *iterable*. + The amount by which the `i`-th item in each tuple is offset is given by + the `i`-th item in *offsets*. + + >>> list(stagger([0, 1, 2, 3])) + [(None, 0, 1), (0, 1, 2), (1, 2, 3)] + >>> list(stagger(range(8), offsets=(0, 2, 4))) + [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] + + By default, the sequence will end when the final element of a tuple is the + last item in the iterable. To continue until the first element of a tuple + is the last item in the iterable, set *longest* to ``True``:: + + >>> list(stagger([0, 1, 2, 3], longest=True)) + [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + children = tee(iterable, len(offsets)) + + return zip_offset( + *children, offsets=offsets, longest=longest, fillvalue=fillvalue + ) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): + """``zip`` the input *iterables* together, but offset the `i`-th iterable + by the `i`-th item in *offsets*. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] + + This can be used as a lightweight alternative to SciPy or pandas to analyze + data sets in which some series have a lead or lag relationship. + + By default, the sequence will end when the shortest iterable is exhausted. + To continue until the longest iterable is exhausted, set *longest* to + ``True``. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + if len(iterables) != len(offsets): + raise ValueError("Number of iterables and offsets didn't match") + + staggered = [] + for it, n in zip(iterables, offsets): + if n < 0: + staggered.append(chain(repeat(fillvalue, -n), it)) + elif n > 0: + staggered.append(islice(it, n, None)) + else: + staggered.append(it) + + if longest: + return zip_longest(*staggered, fillvalue=fillvalue) + + return zip(*staggered) + + +def sort_together(iterables, key_list=(0,), reverse=False): + """Return the input iterables sorted together, with *key_list* as the + priority for sorting. All iterables are trimmed to the length of the + shortest one. + + This can be used like the sorting function in a spreadsheet. If each + iterable represents a column of data, the key list determines which + columns are used for sorting. + + By default, all iterables are sorted using the ``0``-th iterable:: + + >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] + >>> sort_together(iterables) + [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] + + Set a different key list to sort according to another iterable. + Specifying multiple keys dictates how ties are broken:: + + >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] + >>> sort_together(iterables, key_list=(1, 2)) + [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + + Set *reverse* to ``True`` to sort in descending order. + + >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) + [(3, 2, 1), ('a', 'b', 'c')] + + """ + return list( + zip( + *sorted( + zip(*iterables), key=itemgetter(*key_list), reverse=reverse + ) + ) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iter(iterable)) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + def itemgetter(i): + def getter(obj): + try: + return obj[i] + except IndexError: + # basically if we have an iterable like + # iter([(1, 2, 3), (4, 5), (6,)]) + # the second unzipped iterable would fail at the third tuple + # since it would try to access tup[1] + # same with the third unzipped iterable and the second tuple + # to support these "improperly zipped" iterables, + # we create a custom itemgetter + # which just stops the unzipped iterables + # at first length mismatch + raise StopIteration + + return getter + + return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) + + +def divide(n, iterable): + """Divide the elements from *iterable* into *n* parts, maintaining + order. + + >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 2, 3] + >>> list(group_2) + [4, 5, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 2, 3], [4, 5], [6, 7]] + + If the length of the iterable is smaller than n, then the last returned + iterables will be empty: + + >>> children = divide(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function will exhaust the iterable before returning and may require + significant storage. If order is not important, see :func:`distribute`, + which does not first pull the iterable into memory. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + + q, r = divmod(len(seq), n) + + ret = [] + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q + ret.append(iter(seq[start:stop])) + + return ret + + +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +def adjacent(predicate, iterable, distance=1): + """Return an iterable over `(bool, item)` tuples where the `item` is + drawn from *iterable* and the `bool` indicates whether + that item satisfies the *predicate* or is adjacent to an item that does. + + For example, to find whether items are adjacent to a ``3``:: + + >>> list(adjacent(lambda x: x == 3, range(6))) + [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] + + Set *distance* to change what counts as adjacent. For example, to find + whether items are two places away from a ``3``: + + >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) + [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] + + This is useful for contextualizing the results of a search function. + For example, a code comparison tool might want to identify lines that + have changed, but also surrounding lines to give the viewer of the diff + context. + + The predicate function will only be called once for each item in the + iterable. + + See also :func:`groupby_transform`, which can be used with this function + to group ranges of items with the same `bool` value. + + """ + # Allow distance=0 mainly for testing that it reproduces results with map() + if distance < 0: + raise ValueError('distance must be at least 0') + + i1, i2 = tee(iterable) + padding = [False] * distance + selected = chain(padding, map(predicate, i1), padding) + adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) + return zip(adjacent_to_selected, i2) + + +def groupby_transform(iterable, keyfunc=None, valuefunc=None): + """An extension of :func:`itertools.groupby` that transforms the values of + *iterable* after grouping them. + *keyfunc* is a function used to compute a grouping key for each item. + *valuefunc* is a function for transforming the items after grouping. + + >>> iterable = 'AaaABbBCcA' + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: x.lower() + >>> grouper = groupby_transform(iterable, keyfunc, valuefunc) + >>> [(k, ''.join(g)) for k, g in grouper] + [('A', 'aaaa'), ('B', 'bbb'), ('C', 'cc'), ('A', 'a')] + + *keyfunc* and *valuefunc* default to identity functions if they are not + specified. + + :func:`groupby_transform` is useful when grouping elements of an iterable + using a separate iterable as the key. To do this, :func:`zip` the iterables + and pass a *keyfunc* that extracts the first element and a *valuefunc* + that extracts the second element:: + + >>> from operator import itemgetter + >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] + >>> values = 'abcdefghi' + >>> iterable = zip(keys, values) + >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) + >>> [(k, ''.join(g)) for k, g in grouper] + [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] + + Note that the order of items in the iterable is significant. + Only adjacent items are grouped together, so if you don't want any + duplicate groups, you should sort the iterable by the key function. + + """ + res = groupby(iterable, keyfunc) + return ((k, map(valuefunc, g)) for k, g in res) if valuefunc else res + + +def numeric_range(*args): + """An extension of the built-in ``range()`` function whose arguments can + be any orderable numeric type. + + With only *stop* specified, *start* defaults to ``0`` and *step* + defaults to ``1``. The output items will match the type of *stop*: + + >>> list(numeric_range(3.5)) + [0.0, 1.0, 2.0, 3.0] + + With only *start* and *stop* specified, *step* defaults to ``1``. The + output items will match the type of *start*: + + >>> from decimal import Decimal + >>> start = Decimal('2.1') + >>> stop = Decimal('5.1') + >>> list(numeric_range(start, stop)) + [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] + + With *start*, *stop*, and *step* specified the output items will match + the type of ``start + step``: + + >>> from fractions import Fraction + >>> start = Fraction(1, 2) # Start at 1/2 + >>> stop = Fraction(5, 2) # End at 5/2 + >>> step = Fraction(1, 2) # Count by 1/2 + >>> list(numeric_range(start, stop, step)) + [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] + + If *step* is zero, ``ValueError`` is raised. Negative steps are supported: + + >>> list(numeric_range(3, -1, -1.0)) + [3.0, 2.0, 1.0, 0.0] + + Be aware of the limitations of floating point numbers; the representation + of the yielded numbers may be surprising. + + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: + + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = numeric_range(start, stop, step) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + argc = len(args) + if argc == 1: + stop, = args + start = type(stop)(0) + step = 1 + elif argc == 2: + start, stop = args + step = 1 + elif argc == 3: + start, stop, step = args + else: + err_msg = 'numeric_range takes at most 3 arguments, got {}' + raise TypeError(err_msg.format(argc)) + + values = (start + (step * n) for n in count()) + zero = type(step)(0) + if step > zero: + return takewhile(partial(gt, stop), values) + elif step < zero: + return takewhile(partial(lt, stop), values) + else: + raise ValueError('numeric_range arg 3 must not be zero') + + +def count_cycle(iterable, n=None): + """Cycle through the items from *iterable* up to *n* times, yielding + the number of completed cycles along with each item. If *n* is omitted the + process repeats indefinitely. + + >>> list(count_cycle('AB', 3)) + [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] + + """ + iterable = tuple(iterable) + if not iterable: + return iter(()) + counter = count() if n is None else range(n) + return ((i, item) for i in counter for item in iterable) + + +def locate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(locate([0, 1, 1, 0, 1, 0, 0])) + [1, 2, 4] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item. + + >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) + [1, 3] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(locate(iterable, pred=pred, window_size=3)) + [1, 5, 9] + + Use with :func:`seekable` to find indexes and then retrieve the associated + items: + + >>> from itertools import count + >>> from more_itertools import seekable + >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) + >>> it = seekable(source) + >>> pred = lambda x: x > 100 + >>> indexes = locate(it, pred=pred) + >>> i = next(indexes) + >>> it.seek(i) + >>> next(it) + 106 + + """ + if window_size is None: + return compress(count(), map(pred, iterable)) + + if window_size < 1: + raise ValueError('window size must be at least 1') + + it = windowed(iterable, window_size, fillvalue=_marker) + return compress(count(), starmap(pred, it)) + + +def lstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the beginning + for which *pred* returns ``True``. + + For example, to remove a set of items from the start of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(lstrip(iterable, pred)) + [1, 2, None, 3, False, None] + + This function is analogous to to :func:`str.lstrip`, and is essentially + an wrapper for :func:`itertools.dropwhile`. + + """ + return dropwhile(pred, iterable) + + +def rstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the end + for which *pred* returns ``True``. + + For example, to remove a set of items from the end of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(rstrip(iterable, pred)) + [None, False, None, 1, 2, None, 3] + + This function is analogous to :func:`str.rstrip`. + + """ + cache = [] + cache_append = cache.append + cache_clear = cache.clear + for x in iterable: + if pred(x): + cache_append(x) + else: + yield from cache + cache_clear() + yield x + + +def strip(iterable, pred): + """Yield the items from *iterable*, but strip any from the + beginning and end for which *pred* returns ``True``. + + For example, to remove a set of items from both ends of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(strip(iterable, pred)) + [1, 2, None, 3] + + This function is analogous to :func:`str.strip`. + + """ + return rstrip(lstrip(iterable, pred), pred) + + +def islice_extended(iterable, *args): + """An extension of :func:`itertools.islice` that supports negative values + for *stop*, *start*, and *step*. + + >>> iterable = iter('abcdefgh') + >>> list(islice_extended(iterable, -4, -1)) + ['e', 'f', 'g'] + + Slices with negative values require some caching of *iterable*, but this + function takes care to minimize the amount of memory required. + + For example, you can use a negative step with an infinite iterator: + + >>> from itertools import count + >>> list(islice_extended(count(), 110, 99, -2)) + [110, 108, 106, 104, 102, 100] + + """ + s = slice(*args) + start = s.start + stop = s.stop + if s.step == 0: + raise ValueError('step argument must be a non-zero integer or None.') + step = s.step or 1 + + it = iter(iterable) + + if step > 0: + start = 0 if (start is None) else start + + if start < 0: + # Consume all but the last -start items + cache = deque(enumerate(it, 1), maxlen=-start) + len_iter = cache[-1][0] if cache else 0 + + # Adjust start to be positive + i = max(len_iter + start, 0) + + # Adjust stop to be positive + if stop is None: + j = len_iter + elif stop >= 0: + j = min(stop, len_iter) + else: + j = max(len_iter + stop, 0) + + # Slice the cache + n = j - i + if n <= 0: + return + + for index, item in islice(cache, 0, n, step): + yield item + elif (stop is not None) and (stop < 0): + # Advance to the start position + next(islice(it, start, start), None) + + # When stop is negative, we have to carry -stop items while + # iterating + cache = deque(islice(it, -stop), maxlen=-stop) + + for index, item in enumerate(it): + cached_item = cache.popleft() + if index % step == 0: + yield cached_item + cache.append(item) + else: + # When both start and stop are positive we have the normal case + yield from islice(it, start, stop, step) + else: + start = -1 if (start is None) else start + + if (stop is not None) and (stop < 0): + # Consume all but the last items + n = -stop - 1 + cache = deque(enumerate(it, 1), maxlen=n) + len_iter = cache[-1][0] if cache else 0 + + # If start and stop are both negative they are comparable and + # we can just slice. Otherwise we can adjust start to be negative + # and then slice. + if start < 0: + i, j = start, stop + else: + i, j = min(start - len_iter, -1), None + + for index, item in list(cache)[i:j:step]: + yield item + else: + # Advance to the stop position + if stop is not None: + m = stop + 1 + next(islice(it, m, m), None) + + # stop is positive, so if start is negative they are not comparable + # and we need the rest of the items. + if start < 0: + i = start + n = None + # stop is None and start is positive, so we just need items up to + # the start index. + elif stop is None: + i = None + n = start + 1 + # Both stop and start are positive, so they are comparable. + else: + i = None + n = start - stop + if n <= 0: + return + + cache = list(islice(it, n)) + + yield from cache[i::step] + + +def always_reversible(iterable): + """An extension of :func:`reversed` that supports all iterables, not + just those which implement the ``Reversible`` or ``Sequence`` protocols. + + >>> print(*always_reversible(x for x in range(3))) + 2 1 0 + + If the iterable is already reversible, this function returns the + result of :func:`reversed()`. If the iterable is not reversible, + this function will cache the remaining items in the iterable and + yield them in reverse order, which may require significant storage. + """ + try: + return reversed(iterable) + except TypeError: + return reversed(list(iterable)) + + +def consecutive_groups(iterable, ordering=lambda x: x): + """Yield groups of consecutive items using :func:`itertools.groupby`. + The *ordering* function determines whether two items are adjacent by + returning their position. + + By default, the ordering function is the identity function. This is + suitable for finding runs of numbers: + + >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] + >>> for group in consecutive_groups(iterable): + ... print(list(group)) + [1] + [10, 11, 12] + [20] + [30, 31, 32, 33] + [40] + + For finding runs of adjacent letters, try using the :meth:`index` method + of a string of letters: + + >>> from string import ascii_lowercase + >>> iterable = 'abcdfgilmnop' + >>> ordering = ascii_lowercase.index + >>> for group in consecutive_groups(iterable, ordering): + ... print(list(group)) + ['a', 'b', 'c', 'd'] + ['f', 'g'] + ['i'] + ['l', 'm', 'n', 'o', 'p'] + + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + + """ + for k, g in groupby( + enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) + ): + yield map(itemgetter(1), g) + + +def difference(iterable, func=sub, *, initial=None): + """By default, compute the first difference of *iterable* using + :func:`operator.sub`. + + >>> iterable = [0, 1, 3, 6, 10] + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + This is the opposite of :func:`itertools.accumulate`'s default behavior: + + >>> from itertools import accumulate + >>> iterable = [0, 1, 2, 3, 4] + >>> list(accumulate(iterable)) + [0, 1, 3, 6, 10] + >>> list(difference(accumulate(iterable))) + [0, 1, 2, 3, 4] + + By default *func* is :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] # Factorial sequence + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + Since Python 3.8, :func:`itertools.accumulate` can be supplied with an + *initial* keyword argument. If :func:`difference` is called with *initial* + set to something other than ``None``, it will skip the first element when + computing successive differences. + + >>> iterable = [100, 101, 103, 106] # accumate([1, 2, 3], initial=100) + >>> list(difference(iterable, initial=100)) + [1, 2, 3] + + """ + a, b = tee(iterable) + try: + first = [next(b)] + except StopIteration: + return iter([]) + + if initial is not None: + first = [] + + return chain(first, starmap(func, zip(b, a))) + + +class SequenceView(Sequence): + """Return a read-only view of the sequence object *target*. + + :class:`SequenceView` objects are analogous to Python's built-in + "dictionary view" types. They provide a dynamic view of a sequence's items, + meaning that when the sequence updates, so does the view. + + >>> seq = ['0', '1', '2'] + >>> view = SequenceView(seq) + >>> view + SequenceView(['0', '1', '2']) + >>> seq.append('3') + >>> view + SequenceView(['0', '1', '2', '3']) + + Sequence views support indexing, slicing, and length queries. They act + like the underlying sequence, except they don't allow assignment: + + >>> view[1] + '1' + >>> view[1:-1] + ['1', '2'] + >>> len(view) + 4 + + Sequence views are useful as an alternative to copying, as they don't + require (much) extra storage. + + """ + + def __init__(self, target): + if not isinstance(target, Sequence): + raise TypeError + self._target = target + + def __getitem__(self, index): + return self._target[index] + + def __len__(self): + return len(self._target) + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, repr(self._target)) + + +class seekable: + """Wrap an iterator to allow for seeking backward and forward. This + progressively caches the items in the source iterable so they can be + re-visited. + + Call :meth:`seek` with an index to seek to that position in the source + iterable. + + To "reset" an iterator, seek to ``0``: + + >>> from itertools import count + >>> it = seekable((str(n) for n in count())) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.seek(0) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> next(it) + '3' + + You can also seek forward: + + >>> it = seekable((str(n) for n in range(20))) + >>> it.seek(10) + >>> next(it) + '10' + >>> it.seek(20) # Seeking past the end of the source isn't a problem + >>> list(it) + [] + >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it), next(it), next(it) + ('0', '1', '2') + + The cache grows as the source iterable progresses, so beware of wrapping + very large or infinite iterables. + + You may view the contents of the cache with the :meth:`elements` method. + That returns a :class:`SequenceView`, a view that updates automatically: + + >>> it = seekable((str(n) for n in range(10))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> elements = it.elements() + >>> elements + SequenceView(['0', '1', '2']) + >>> next(it) + '3' + >>> elements + SequenceView(['0', '1', '2', '3']) + + """ + + def __init__(self, iterable): + self._source = iter(iterable) + self._cache = [] + self._index = None + + def __iter__(self): + return self + + def __next__(self): + if self._index is not None: + try: + item = self._cache[self._index] + except IndexError: + self._index = None + else: + self._index += 1 + return item + + item = next(self._source) + self._cache.append(item) + return item + + def elements(self): + return SequenceView(self._cache) + + def seek(self, index): + self._index = index + remainder = index - len(self._cache) + if remainder > 0: + consume(self, remainder) + + +class run_length: + """ + :func:`run_length.encode` compresses an iterable with run-length encoding. + It yields groups of repeated items with the count of how many times they + were repeated: + + >>> uncompressed = 'abbcccdddd' + >>> list(run_length.encode(uncompressed)) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + + :func:`run_length.decode` decompresses an iterable that was previously + compressed with run-length encoding. It yields the items of the + decompressed iterable: + + >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> list(run_length.decode(compressed)) + ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] + + """ + + @staticmethod + def encode(iterable): + return ((k, ilen(g)) for k, g in groupby(iterable)) + + @staticmethod + def decode(iterable): + return chain.from_iterable(repeat(k, n) for k, n in iterable) + + +def exactly_n(iterable, n, predicate=bool): + """Return ``True`` if exactly ``n`` items in the iterable are ``True`` + according to the *predicate* function. + + >>> exactly_n([True, True, False], 2) + True + >>> exactly_n([True, True, False], 1) + False + >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) + True + + The iterable will be advanced until ``n + 1`` truthy items are encountered, + so avoid calling it on infinite iterables. + + """ + return len(take(n + 1, filter(predicate, iterable))) == n + + +def circular_shifts(iterable): + """Return a list of circular shifts of *iterable*. + + >>> circular_shifts(range(4)) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + """ + lst = list(iterable) + return take(len(lst), windowed(cycle(lst), len(lst))) + + +def make_decorator(wrapping_func, result_index=0): + """Return a decorator version of *wrapping_func*, which is a function that + modifies an iterable. *result_index* is the position in that function's + signature where the iterable goes. + + This lets you use itertools on the "production end," i.e. at function + definition. This can augment what the function returns without changing the + function's code. + + For example, to produce a decorator version of :func:`chunked`: + + >>> from more_itertools import chunked + >>> chunker = make_decorator(chunked, result_index=0) + >>> @chunker(3) + ... def iter_range(n): + ... return iter(range(n)) + ... + >>> list(iter_range(9)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + To only allow truthy items to be returned: + + >>> truth_serum = make_decorator(filter, result_index=1) + >>> @truth_serum(bool) + ... def boolean_test(): + ... return [0, 1, '', ' ', False, True] + ... + >>> list(boolean_test()) + [1, ' ', True] + + The :func:`peekable` and :func:`seekable` wrappers make for practical + decorators: + + >>> from more_itertools import peekable + >>> peekable_function = make_decorator(peekable) + >>> @peekable_function() + ... def str_range(*args): + ... return (str(x) for x in range(*args)) + ... + >>> it = str_range(1, 20, 2) + >>> next(it), next(it), next(it) + ('1', '3', '5') + >>> it.peek() + '7' + >>> next(it) + '7' + + """ + # See https://sites.google.com/site/bbayles/index/decorator_factory for + # notes on how this works. + def decorator(*wrapping_args, **wrapping_kwargs): + def outer_wrapper(f): + def inner_wrapper(*args, **kwargs): + result = f(*args, **kwargs) + wrapping_args_ = list(wrapping_args) + wrapping_args_.insert(result_index, result) + return wrapping_func(*wrapping_args_, **wrapping_kwargs) + + return inner_wrapper + + return outer_wrapper + + return decorator + + +def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): + """Return a dictionary that maps the items in *iterable* to categories + defined by *keyfunc*, transforms them with *valuefunc*, and + then summarizes them by category with *reducefunc*. + + *valuefunc* defaults to the identity function if it is unspecified. + If *reducefunc* is unspecified, no summarization takes place: + + >>> keyfunc = lambda x: x.upper() + >>> result = map_reduce('abbccc', keyfunc) + >>> sorted(result.items()) + [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] + + Specifying *valuefunc* transforms the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> result = map_reduce('abbccc', keyfunc, valuefunc) + >>> sorted(result.items()) + [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] + + Specifying *reducefunc* summarizes the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> reducefunc = sum + >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) + >>> sorted(result.items()) + [('A', 1), ('B', 2), ('C', 3)] + + You may want to filter the input iterable before applying the map/reduce + procedure: + + >>> all_items = range(30) + >>> items = [x for x in all_items if 10 <= x <= 20] # Filter + >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 + >>> categories = map_reduce(items, keyfunc=keyfunc) + >>> sorted(categories.items()) + [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] + >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) + >>> sorted(summaries.items()) + [(0, 90), (1, 75)] + + Note that all items in the iterable are gathered into a list before the + summarization step, which may require significant storage. + + The returned object is a :obj:`collections.defaultdict` with the + ``default_factory`` set to ``None``, such that it behaves like a normal + dictionary. + + """ + valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc + + ret = defaultdict(list) + for item in iterable: + key = keyfunc(item) + value = valuefunc(item) + ret[key].append(value) + + if reducefunc is not None: + for key, value_list in ret.items(): + ret[key] = reducefunc(value_list) + + ret.default_factory = None + return ret + + +def rlocate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``, starting from the right and moving left. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 + [4, 2, 1] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item: + + >>> iterable = iter('abcb') + >>> pred = lambda x: x == 'b' + >>> list(rlocate(iterable, pred)) + [3, 1] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(rlocate(iterable, pred=pred, window_size=3)) + [9, 5, 1] + + Beware, this function won't return anything for infinite iterables. + If *iterable* is reversible, ``rlocate`` will reverse it and search from + the right. Otherwise, it will search from the left and return the results + in reverse order. + + See :func:`locate` to for other example applications. + + """ + if window_size is None: + try: + len_iter = len(iterable) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) + except TypeError: + pass + + return reversed(list(locate(iterable, pred, window_size))) + + +def replace(iterable, pred, substitutes, count=None, window_size=1): + """Yield the items from *iterable*, replacing the items for which *pred* + returns ``True`` with the items from the iterable *substitutes*. + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] + >>> pred = lambda x: x == 0 + >>> substitutes = (2, 3) + >>> list(replace(iterable, pred, substitutes)) + [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] + + If *count* is given, the number of replacements will be limited: + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] + >>> pred = lambda x: x == 0 + >>> substitutes = [None] + >>> list(replace(iterable, pred, substitutes, count=2)) + [1, 1, None, 1, 1, None, 1, 1, 0] + + Use *window_size* to control the number of items passed as arguments to + *pred*. This allows for locating and replacing subsequences. + + >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] + >>> window_size = 3 + >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred + >>> substitutes = [3, 4] # Splice in these items + >>> list(replace(iterable, pred, substitutes, window_size=window_size)) + [3, 4, 5, 3, 4, 5] + + """ + if window_size < 1: + raise ValueError('window_size must be at least 1') + + # Save the substitutes iterable, since it's used more than once + substitutes = tuple(substitutes) + + # Add padding such that the number of windows matches the length of the + # iterable + it = chain(iterable, [_marker] * (window_size - 1)) + windows = windowed(it, window_size) + + n = 0 + for w in windows: + # If the current window matches our predicate (and we haven't hit + # our maximum number of replacements), splice in the substitutes + # and then consume the following windows that overlap with this one. + # For example, if the iterable is (0, 1, 2, 3, 4...) + # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... + # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) + if pred(*w): + if (count is None) or (n < count): + n += 1 + yield from substitutes + consume(windows, window_size - 1) + continue + + # If there was no match (or we've reached the replacement limit), + # yield the first item from the window. + if w and (w[0] is not _marker): + yield w[0] + + +def partitions(iterable): + """Yield all possible order-perserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from set_partitions_helper(L, k) + else: + yield from set_partitions_helper(L, k) + + +def time_limited(limit_seconds, iterable): + """ + Yield items from *iterable* until *limit_seconds* have passed. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = generator() + >>> list(time_limited(0.1, iterable)) + [1, 2] + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + + """ + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + + start_time = monotonic() + for item in iterable: + if monotonic() - start_time > limit_seconds: + break + yield item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + source = iter(iterable) + + while True: + # Check to see whether we're at the end of the source iterable + item = next(source, _marker) + if item is _marker: + return + + # Clone the source and yield an n-length slice + source, it = tee(chain([item], source)) + yield islice(it, n) + + # Advance the source iterable + consume(source, n) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + else: + pool = tuple(iterable) + for i, prefix in unique_everseen(enumerate(pool), key=itemgetter(1)): + for suffix in distinct_combinations(pool[i + 1 :], r - 1): + yield (prefix,) + suffix + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + exceptions = tuple(exceptions) + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should be a accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + exceptions = tuple(exceptions) + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def _sample_unweighted(iterable, k): + # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + # Fill up the reservoir (collection of samples) with the first `k` samples + reservoir = take(k, iterable) + + # Generate random number that's the largest in a sample of k U(0,1) numbers + # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic + W = exp(log(random()) / k) + + # The number of elements to skip before changing the reservoir is a random + # number with a geometric distribution. Sample it using random() and logs. + next_index = k + floor(log(random()) / log(1 - W)) + + for index, element in enumerate(iterable, k): + + if index == next_index: + reservoir[randrange(k)] = element + # The new W is the largest in a sample of k U(0, `old_W`) numbers + W *= exp(log(random()) / k) + next_index += floor(log(random()) / log(1 - W)) + 1 + + return reservoir + + +def _sample_weighted(iterable, k, weights): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterable)) + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterable): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + # Equivalent to [element for weight_key, element in sorted(reservoir)] + return [heappop(reservoir)[1] for _ in range(k)] + + +def sample(iterable, k, weights=None): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. Like :func:`random.sample`, but works on iterables + of unknown length. + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + An iterable with *weights* may also be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + The algorithm can also be used to generate weighted random permutations. + The relative weight of each item determines the probability that it + appears late in the permutation. + + >>> data = "abcdefgh" + >>> weights = range(1, len(data) + 1) + >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP + ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + """ + if k == 0: + return [] + + iterable = iter(iterable) + if weights is None: + return _sample_unweighted(iterable, k) + else: + weights = iter(weights) + return _sample_weighted(iterable, k, weights) diff --git a/venv/lib/python3.7/site-packages/more_itertools/more.pyi b/venv/lib/python3.7/site-packages/more_itertools/more.pyi new file mode 100644 index 0000000..0e281ad --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/more.pyi @@ -0,0 +1,367 @@ +"""Stubs for more_itertools.more""" + +from typing import ( + Any, + Callable, + Container, + Dict, + Generic, + Iterable, + Iterator, + List, + Optional, + Reversible, + Sequence, + Sized, + Tuple, + Union, + TypeVar, + type_check_only, +) +from typing_extensions import ContextManager, Protocol, Type, overload + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) +_Raisable = Union[BaseException, 'Type[BaseException]'] +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +def chunked(iterable: Iterable[_T], n: int) -> Iterator[List[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... + +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> List[_T]: ... + +def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[object]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: ContextManager[Iterable[_T]] +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: Optional[_Raisable] = ..., + too_long: Optional[_Raisable] = ..., +) -> _T: ... +def distinct_permutations( + iterable: Iterable[_T] +) -> Iterator[Tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[Union[_T, _U]]: ... +def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[Tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Optional[Callable[[object], object]] = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> Tuple[List[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: Optional[type] = ..., + levels: Optional[int] = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[List[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +def sliced(seq: Sequence[_T], n: int) -> Iterator[Sequence[_T]]: ... +def split_at( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[List[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[List[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[List[_T]]: ... +def split_when( + iterable: Iterable[_T], pred: Callable[[_T, _T], object] +) -> Iterator[List[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[Optional[int]] +) -> Iterator[List[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: Optional[int] = ..., + next_multiple: bool = ... +) -> Iterator[Optional[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: Optional[int] = ..., + next_multiple: bool = ..., +) -> Iterator[Union[_T, _U]]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last(iterable: Iterable[_T], default: _U) -> Iterator[Union[_T, _U]]: ... +def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], offsets: _SizedIterable[int], longest: bool = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + reverse: bool = ..., +) -> List[Tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +def always_iterable( + obj: object, base_type: Union[type, Tuple[type, ...], None] = ... +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[Tuple[bool, _T]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], keyfunc: None = ..., valuefunc: None = ... +) -> Iterator[Tuple[_T, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: None = ... +) -> Iterator[Tuple[_U, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None = ..., + valuefunc: Callable[[_T], _V] = ..., +) -> Iterator[Tuple[_T, Iterator[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], +) -> Iterator[Tuple[_U, Iterator[_V]]]: ... +def numeric_range(*args: Any) -> Iterator[Any]: ... +def count_cycle( + iterable: Iterable[_T], n: Optional[int] = ... +) -> Iterable[Tuple[int, _T]]: ... +def locate( + iterable: Iterable[object], + pred: Callable[..., Any] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def islice_extended( + iterable: Iterable[_T], *args: Optional[int] +) -> Iterator[_T]: ... +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ... +) -> Iterator[Union[_T, _U]]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> Dict[_U, List[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> Dict[_U, List[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[List[_T]], _W] = ..., +) -> Dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[List[_V]], _W], +) -> Dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: Optional[int] = ..., + window_size: int = ..., +) -> Iterator[Union[_T, _U]]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], k: Optional[int] = ... +) -> Iterator[List[List[_T]]]: ... +def time_limited( + limit_seconds: float, iterable: Iterable[_T] +) -> Iterator[_T]: ... +@overload +def only( + iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... +) -> Optional[_T]: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... +) -> Union[_T, _U]: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[Tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_U]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Iterable[_T], +) -> List[_T]: ... diff --git a/venv/lib/python3.7/site-packages/more_itertools/py.typed b/venv/lib/python3.7/site-packages/more_itertools/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.7/site-packages/more_itertools/recipes.py b/venv/lib/python3.7/site-packages/more_itertools/recipes.py new file mode 100644 index 0000000..77cc2db --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/recipes.py @@ -0,0 +1,572 @@ +"""Imported from the recipes section of the itertools documentation. + +All functions taken from the recipes section of the itertools library docs +[1]_. +Some backward-compatible usability improvements have been made. + +.. [1] http://docs.python.org/library/itertools.html#recipes + +""" +import warnings +from collections import deque +from itertools import ( + chain, + combinations, + count, + cycle, + groupby, + islice, + repeat, + starmap, + tee, + zip_longest, +) +import operator +from random import randrange, sample, choice + +__all__ = [ + 'all_equal', + 'consume', + 'dotproduct', + 'first_true', + 'flatten', + 'grouper', + 'iter_except', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pairwise', + 'partition', + 'powerset', + 'prepend', + 'quantify', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'tabulate', + 'tail', + 'take', + 'unique_everseen', + 'unique_justseen', +] + + +def take(n, iterable): + """Return first *n* items of the iterable as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +def tabulate(function, start=0): + """Return an iterator over the results of ``func(start)``, + ``func(start + 1)``, ``func(start + 2)``... + + *func* should be a function that accepts one integer argument. + + If *start* is not specified it defaults to 0. It will be incremented each + time the iterator is advanced. + + >>> square = lambda x: x ** 2 + >>> iterator = tabulate(square, -3) + >>> take(4, iterator) + [9, 4, 1, 0] + + """ + return map(function, count(start)) + + +def tail(n, iterable): + """Return an iterator over the last *n* items of *iterable*. + + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] + + """ + return iter(deque(iterable, maxlen=n)) + + +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' + + """ + return next(islice(iterable, n, None), default) + + +def all_equal(iterable): + """ + Returns ``True`` if all the elements are equal to each other. + + >>> all_equal('aaaa') + True + >>> all_equal('aaab') + False + + """ + g = groupby(iterable) + return next(g, True) and not next(g, False) + + +def quantify(iterable, pred=bool): + """Return the how many times the predicate is true. + + >>> quantify([True, False, True]) + 2 + + """ + return sum(map(pred, iterable)) + + +def padnone(iterable): + """Returns the sequence of elements and then returns ``None`` indefinitely. + + >>> take(5, padnone(range(3))) + [0, 1, 2, None, None] + + Useful for emulating the behavior of the built-in :func:`map` function. + + See also :func:`padded`. + + """ + return chain(iterable, repeat(None)) + + +def ncycles(iterable, n): + """Returns the sequence elements *n* times + + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] + + """ + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def dotproduct(vec1, vec2): + """Returns the dot product of the two iterables. + + >>> dotproduct([10, 10], [20, 20]) + 400 + + """ + return sum(map(operator.mul, vec1, vec2)) + + +def flatten(listOfLists): + """Return an iterator flattening one level of nesting in a list of lists. + + >>> list(flatten([[0, 1], [2, 3]])) + [0, 1, 2, 3] + + See also :func:`collapse`, which can flatten multiple levels of nesting. + + """ + return chain.from_iterable(listOfLists) + + +def repeatfunc(func, times=None, *args): + """Call *func* with *args* repeatedly, returning an iterable over the + results. + + If *times* is specified, the iterable will terminate after that many + repetitions: + + >>> from operator import add + >>> times = 4 + >>> args = 3, 5 + >>> list(repeatfunc(add, times, *args)) + [8, 8, 8, 8] + + If *times* is ``None`` the iterable will not terminate: + + >>> from random import randrange + >>> times = None + >>> args = 1, 11 + >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP + [2, 4, 8, 1, 8, 4] + + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def pairwise(iterable): + """Returns an iterator of paired items, overlapping, from the original + + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + """ + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks. + + >>> list(grouper('ABCDEFG', 3, 'x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + """ + if isinstance(iterable, int): + warnings.warn( + "grouper expects iterable as first parameter", DeprecationWarning + ) + n, iterable = iterable, n + args = [iter(iterable)] * n + return zip_longest(fillvalue=fillvalue, *args) + + +def roundrobin(*iterables): + """Yields an item from each iterable, alternating between them. + + >>> list(roundrobin('ABC', 'D', 'EF')) + ['A', 'D', 'E', 'B', 'F', 'C'] + + This function produces the same output as :func:`interleave_longest`, but + may perform better for some inputs (in particular when the number of + iterables is small). + + """ + # Recipe credited to George Sakkis + pending = len(iterables) + nexts = cycle(iter(it).__next__ for it in iterables) + while pending: + try: + for next in nexts: + yield next() + except StopIteration: + pending -= 1 + nexts = cycle(islice(nexts, pending)) + + +def partition(pred, iterable): + """ + Returns a 2-tuple of iterables derived from the input iterable. + The first yields the items that have ``pred(item) == False``. + The second yields the items that have ``pred(item) == True``. + + >>> is_odd = lambda x: x % 2 != 0 + >>> iterable = range(10) + >>> even_items, odd_items = partition(is_odd, iterable) + >>> list(even_items), list(odd_items) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + + """ + if pred is None: + pred = bool + + evaluations = ((pred(x), x) for x in iterable) + t1, t2 = tee(evaluations) + return ( + (x for (cond, x) in t1 if not cond), + (x for (cond, x) in t2 if cond), + ) + + +def powerset(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset([1, 2, 3])) + [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. Use :func:`unique_everseen` on the input to avoid generating + duplicates: + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + >>> from more_itertools import unique_everseen + >>> list(powerset(unique_everseen(seq))) + [(), (1,), (0,), (1, 0)] + + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def unique_everseen(iterable, key=None): + """ + Yield unique elements, preserving order. + + >>> list(unique_everseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D'] + >>> list(unique_everseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'D'] + + Sequences with a mix of hashable and unhashable items can be used. + The function will be slower (i.e., `O(n^2)`) for unhashable items. + + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similary, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + iterable, keys = tee(iterable) + for element, k in zip(iterable, map(key, keys) if key else keys): + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element + + +def unique_justseen(iterable, key=None): + """Yields elements in order, ignoring serial duplicates + + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] + + """ + return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + + +def iter_except(func, exception, first=None): + """Yields results from a function repeatedly until an exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel + to end the loop. + + >>> l = [0, 1, 2] + >>> list(iter_except(l.pop, IndexError)) + [2, 1, 0] + + """ + try: + if first is not None: + yield first() + while 1: + yield func() + except exception: + pass + + +def first_true(iterable, default=None, pred=None): + """ + Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which + ``pred(item) == True`` . + + >>> first_true(range(10)) + 1 + >>> first_true(range(10), pred=lambda x: x > 5) + 6 + >>> first_true(range(10), default='missing', pred=lambda x: x > 9) + 'missing' + + """ + return next(filter(pred, iterable), default) + + +def random_product(*args, repeat=1): + """Draw an item at random from each of the input iterables. + + >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP + ('c', 3, 'Z') + + If *repeat* is provided as a keyword argument, that many items will be + drawn from each iterable. + + >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP + ('a', 2, 'd', 3) + + This equivalent to taking a random selection from + ``itertools.product(*args, **kwarg)``. + + """ + pools = [tuple(pool) for pool in args] * repeat + return tuple(choice(pool) for pool in pools) + + +def random_permutation(iterable, r=None): + """Return a random *r* length permutation of the elements in *iterable*. + + If *r* is not specified or is ``None``, then *r* defaults to the length of + *iterable*. + + >>> random_permutation(range(5)) # doctest:+SKIP + (3, 4, 0, 1, 2) + + This equivalent to taking a random selection from + ``itertools.permutations(iterable, r)``. + + """ + pool = tuple(iterable) + r = len(pool) if r is None else r + return tuple(sample(pool, r)) + + +def random_combination(iterable, r): + """Return a random *r* length subsequence of the elements in *iterable*. + + >>> random_combination(range(5), 3) # doctest:+SKIP + (2, 3, 4) + + This equivalent to taking a random selection from + ``itertools.combinations(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(sample(range(n), r)) + return tuple(pool[i] for i in indices) + + +def random_combination_with_replacement(iterable, r): + """Return a random *r* length subsequence of elements in *iterable*, + allowing individual elements to be repeated. + + >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP + (0, 0, 1, 2, 2) + + This equivalent to taking a random selection from + ``itertools.combinations_with_replacement(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(randrange(n) for i in range(r)) + return tuple(pool[i] for i in indices) + + +def nth_combination(iterable, r, index): + """Equivalent to ``list(combinations(iterable, r))[index]``. + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`nth_combination` computes the subsequence at + sort position *index* directly, without computing the previous + subsequences. + + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = 1 + k = min(r, n - r) + for i in range(1, k + 1): + c = c * (n - k + i) // i + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + + return tuple(result) + + +def prepend(value, iterator): + """Yield *value*, followed by the elements in *iterator*. + + >>> value = '0' + >>> iterator = ['1', '2', '3'] + >>> list(prepend(value, iterator)) + ['0', '1', '2', '3'] + + To prepend multiple values, see :func:`itertools.chain`. + + """ + return chain([value], iterator) diff --git a/venv/lib/python3.7/site-packages/more_itertools/recipes.pyi b/venv/lib/python3.7/site-packages/more_itertools/recipes.pyi new file mode 100644 index 0000000..1a614e5 --- /dev/null +++ b/venv/lib/python3.7/site-packages/more_itertools/recipes.pyi @@ -0,0 +1,100 @@ +"""Stubs for more_itertools.recipes""" + +from typing import ( + Any, + Callable, + Iterable, + Iterator, + List, + Optional, + Tuple, + TypeVar, + Union, +) +from typing_extensions import overload, Type + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + +def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... +def all_equal(iterable: Iterable[object]) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: Optional[int] = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... +@overload +def grouper(iterable: Iterable[_T], n: int) -> Iterator[Optional[_T]]: ... +@overload +def grouper( + iterable: Iterable[_T], n: int, fillvalue: _U +) -> Iterator[Union[_T, _U]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T] +) -> Iterator[Optional[_T]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T], fillvalue: _U +) -> Iterator[Union[_T, _U]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Callable[[_T], object], iterable: Iterable[_T] +) -> Tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], exception: Type[BaseException], first: None = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Type[BaseException], + first: Callable[[], _U], +) -> Iterator[Union[_T, _U]]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... +) -> Optional[_T]: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Optional[Callable[[_T], object]] = ..., +) -> Union[_T, _U]: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> Tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> Tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/LICENSE b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/LICENSE new file mode 100644 index 0000000..353924b --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/METADATA b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/METADATA new file mode 100644 index 0000000..6a6fdd1 --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/METADATA @@ -0,0 +1,51 @@ +Metadata-Version: 2.1 +Name: zipp +Version: 2.0.0 +Summary: Backport of pathlib-compatible object wrapper for zip files +Home-page: https://github.com/jaraco/zipp +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +License: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.6 +Requires-Dist: more-itertools +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=3.2) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Provides-Extra: testing +Requires-Dist: pathlib2 ; extra == 'testing' +Requires-Dist: contextlib2 ; extra == 'testing' +Requires-Dist: unittest2 ; extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/zipp.svg + :target: https://pypi.org/project/zipp + +.. image:: https://img.shields.io/pypi/pyversions/zipp.svg + +.. image:: https://dev.azure.com/jaraco/zipp/_apis/build/status/jaraco.zipp?branchName=master + :target: https://dev.azure.com/jaraco/zipp/_build/latest?definitionId=1&branchName=master + +.. image:: https://img.shields.io/travis/jaraco/zipp/master.svg + :target: https://travis-ci.org/jaraco/zipp + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. image:: https://img.shields.io/appveyor/ci/jaraco/zipp/master.svg + :target: https://ci.appveyor.com/project/jaraco/zipp/branch/master + +.. .. image:: https://readthedocs.org/projects/zipp/badge/?version=latest +.. :target: https://zipp.readthedocs.io/en/latest/?badge=latest + + +A pathlib-compatible Zipfile object wrapper. A backport of the +`Path object `_. + + diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/RECORD b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/RECORD new file mode 100644 index 0000000..8cf1f26 --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/zipp.cpython-37.pyc,, +zipp-2.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +zipp-2.0.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +zipp-2.0.0.dist-info/METADATA,sha256=tcJVUM7hrdVCWQ11uIq7xdtd_C0JvyJx76DH6mFO2vM,1912 +zipp-2.0.0.dist-info/RECORD,, +zipp-2.0.0.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92 +zipp-2.0.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5 +zipp.py,sha256=at2n-fuj-jXv7XytlGT0wNMpVEyILk-O15wXSarOQww,5011 diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/WHEEL b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/WHEEL new file mode 100644 index 0000000..3b5c403 --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/top_level.txt new file mode 100644 index 0000000..e82f676 --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp-2.0.0.dist-info/top_level.txt @@ -0,0 +1 @@ +zipp diff --git a/venv/lib/python3.7/site-packages/zipp.py b/venv/lib/python3.7/site-packages/zipp.py new file mode 100644 index 0000000..8ab7d09 --- /dev/null +++ b/venv/lib/python3.7/site-packages/zipp.py @@ -0,0 +1,220 @@ +# coding: utf-8 + +from __future__ import division + +import io +import sys +import posixpath +import zipfile +import functools +import itertools + +import more_itertools + +__metaclass__ = type + + +def _parents(path): + """ + Given a path with elements separated by + posixpath.sep, generate all parents of that path. + + >>> list(_parents('b/d')) + ['b'] + >>> list(_parents('/b/d/')) + ['/b'] + >>> list(_parents('b/d/f/')) + ['b/d', 'b'] + >>> list(_parents('b')) + [] + >>> list(_parents('')) + [] + """ + return itertools.islice(_ancestry(path), 1, None) + + +def _ancestry(path): + """ + Given a path with elements separated by + posixpath.sep, generate all elements of that path + + >>> list(_ancestry('b/d')) + ['b/d', 'b'] + >>> list(_ancestry('/b/d/')) + ['/b/d', '/b'] + >>> list(_ancestry('b/d/f/')) + ['b/d/f', 'b/d', 'b'] + >>> list(_ancestry('b')) + ['b'] + >>> list(_ancestry('')) + [] + """ + path = path.rstrip(posixpath.sep) + while path and path != posixpath.sep: + yield path + path, tail = posixpath.split(path) + + +class Path: + """ + A pathlib-compatible interface for zip files. + + Consider a zip file with this structure:: + + . + ├── a.txt + └── b + ├── c.txt + └── d + └── e.txt + + >>> data = io.BytesIO() + >>> zf = zipfile.ZipFile(data, 'w') + >>> zf.writestr('a.txt', 'content of a') + >>> zf.writestr('b/c.txt', 'content of c') + >>> zf.writestr('b/d/e.txt', 'content of e') + >>> zf.filename = 'abcde.zip' + + Path accepts the zipfile object itself or a filename + + >>> root = Path(zf) + + From there, several path operations are available. + + Directory iteration (including the zip file itself): + + >>> a, b = root.iterdir() + >>> a + Path('abcde.zip', 'a.txt') + >>> b + Path('abcde.zip', 'b/') + + name property: + + >>> b.name + 'b' + + join with divide operator: + + >>> c = b / 'c.txt' + >>> c + Path('abcde.zip', 'b/c.txt') + >>> c.name + 'c.txt' + + Read text: + + >>> c.read_text() + 'content of c' + + existence: + + >>> c.exists() + True + >>> (b / 'missing.txt').exists() + False + + Coercion to string: + + >>> str(c) + 'abcde.zip/b/c.txt' + """ + + __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" + + def __init__(self, root, at=""): + self.root = ( + root + if isinstance(root, zipfile.ZipFile) + else zipfile.ZipFile(self._pathlib_compat(root)) + ) + self.at = at + + @staticmethod + def _pathlib_compat(path): + """ + For path-like objects, convert to a filename for compatibility + on Python 3.6.1 and earlier. + """ + try: + return path.__fspath__() + except AttributeError: + return str(path) + + @property + def open(self): + return functools.partial(self.root.open, self.at) + + @property + def name(self): + return posixpath.basename(self.at.rstrip("/")) + + def read_text(self, *args, **kwargs): + with self.open() as strm: + return io.TextIOWrapper(strm, *args, **kwargs).read() + + def read_bytes(self): + with self.open() as strm: + return strm.read() + + def _is_child(self, path): + return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") + + def _next(self, at): + return Path(self.root, at) + + def is_dir(self): + return not self.at or self.at.endswith("/") + + def is_file(self): + return not self.is_dir() + + def exists(self): + return self.at in self._names() + + def iterdir(self): + if not self.is_dir(): + raise ValueError("Can't listdir a file") + subs = map(self._next, self._names()) + return filter(self._is_child, subs) + + def __str__(self): + return posixpath.join(self.root.filename, self.at) + + def __repr__(self): + return self.__repr.format(self=self) + + def joinpath(self, add): + add = self._pathlib_compat(add) + next = posixpath.join(self.at, add) + next_dir = posixpath.join(self.at, add, "") + names = self._names() + return self._next(next_dir if next not in names and next_dir in names else next) + + __truediv__ = joinpath + + @staticmethod + def _implied_dirs(names): + return more_itertools.unique_everseen( + parent + "/" + for name in names + for parent in _parents(name) + if parent + "/" not in names + ) + + @classmethod + def _add_implied_dirs(cls, names): + return names + list(cls._implied_dirs(names)) + + @property + def parent(self): + parent_at = posixpath.dirname(self.at.rstrip('/')) + if parent_at: + parent_at += '/' + return self._next(parent_at) + + def _names(self): + return self._add_implied_dirs(self.root.namelist()) + + if sys.version_info < (3,): + __div__ = __truediv__